Merge "Update FakedFont to use shared pointer" into sc-dev
diff --git a/Android.bp b/Android.bp
index 202e548..2ccadd3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -760,6 +760,7 @@
     srcs: [
         ":ipconnectivity-proto-src",
         ":libstats_atom_enum_protos",
+        ":libtombstone_proto-src",
         "core/proto/**/*.proto",
         "libs/incident/**/*.proto",
         ":service-permission-protos",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 49433f1..86364af 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -82,12 +82,13 @@
         "android.hardware.vibrator-V1.3-java",
         "framework-protos",
         "stable.core.platform.api.stubs",
-        // There are a few classes from modules used as type arguments that
-        // need to be resolved by metalava. For now, we can use a previously
-        // finalized stub library to resolve them. If a new class gets added,
-        // this may be need to be revisited to use a manually maintained stub
-        // library with empty classes in order to resolve those references.
-        "sdk_system_30_android",
+        // There are a few classes from modules used by the core that
+        // need to be resolved by metalava. We use a prebuilt stub of the
+        // full sdk to ensure we can resolve them. If a new class gets added,
+        // the prebuilts/sdk/current needs to be updated.
+        "sdk_system_current_android",
+        // NOTE: The below can be removed once the prebuilt stub contains IKE.
+        "sdk_system_current_android.net.ipsec.ike",
     ],
     high_mem: true, // Lots of sources => high memory use, see b/170701554
     installable: false,
@@ -404,7 +405,11 @@
         "android_defaults_stubs_current",
         "android_stubs_dists_default",
     ],
-    libs: ["sdk_system_29_android"],
+    libs: [
+        "sdk_system_current_android",
+        // NOTE: The below can be removed once the prebuilt stub contains IKE.
+        "sdk_system_current_android.net.ipsec.ike",
+    ],
     static_libs: ["art.module.public.api.stubs"],
     dist: {
         dir: "apistubs/android/module-lib",
diff --git a/apct-tests/perftests/core/OWNERS b/apct-tests/perftests/core/OWNERS
new file mode 100644
index 0000000..18486af
--- /dev/null
+++ b/apct-tests/perftests/core/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index c37f6d9..a2dc1c2 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -19,14 +19,12 @@
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 
-import android.graphics.Rect;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.perftests.utils.ManualBenchmarkState;
 import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
 import android.perftests.utils.PerfManualStatusReporter;
 import android.view.Display;
-import android.view.DisplayCutout;
 import android.view.IWindowSession;
 import android.view.InputChannel;
 import android.view.InsetsSourceControl;
@@ -85,9 +83,6 @@
     private static class TestWindow extends BaseIWindow {
         final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
         final InsetsState mRequestedVisibility = new InsetsState();
-        final Rect mOutFrame = new Rect();
-        final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
         final InsetsState mOutInsetsState = new InsetsState();
         final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
 
@@ -107,7 +102,7 @@
 
                 long startTime = SystemClock.elapsedRealtimeNanos();
                 session.addToDisplay(this, mLayoutParams, View.VISIBLE,
-                        Display.DEFAULT_DISPLAY, mRequestedVisibility, mOutFrame, inputChannel,
+                        Display.DEFAULT_DISPLAY, mRequestedVisibility, inputChannel,
                         mOutInsetsState, mOutControls);
                 final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
                 state.addExtraResult("add", elapsedTimeNsOfAdd);
diff --git a/apex/Android.bp b/apex/Android.bp
index 1876110..8310ba7 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -15,189 +15,3 @@
 package {
     default_visibility: [":__subpackages__"],
 }
-
-mainline_stubs_args =
-    "--error UnhiddenSystemApi " +
-    "--hide BroadcastBehavior " +
-    "--hide CallbackInterface " +
-    "--hide DeprecationMismatch " +
-    "--hide HiddenSuperclass " +
-    "--hide HiddenTypedefConstant " +
-    "--hide HiddenTypeParameter " +
-    "--hide MissingPermission " +
-    "--hide RequiresPermission " +
-    "--hide SdkConstant " +
-    "--hide Todo " +
-    "--hide Typo " +
-    "--hide UnavailableSymbol "
-
-// TODO: modularize this so not every module has the same whitelist
-framework_packages_to_document = [
-    "android",
-    "dalvik",
-    "java",
-    "javax",
-    "junit",
-    "org.apache.http",
-    "org.json",
-    "org.w3c.dom",
-    "org.xml.sax",
-    "org.xmlpull",
-]
-
-// TODO: remove the hiding when server classes are cleaned up.
-mainline_framework_stubs_args =
-    mainline_stubs_args +
-    "--hide-package com.android.server "
-
-priv_apps = " " +
-    "--show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
-    "\\) "
-
-module_libs = " " +
-    " --show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
-    "\\)" +
-    " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
-    "\\) "
-
-mainline_service_stubs_args =
-    mainline_stubs_args +
-    "--show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.SYSTEM_SERVER" +
-    "\\) " +
-    "--hide-annotation android.annotation.Hide " +
-    "--hide InternalClasses " // com.android.* classes are okay in this interface
-
-// Defaults common to all mainline module java_sdk_library instances.
-java_defaults {
-    name: "framework-module-common-defaults",
-
-    // Additional annotations used for compiling both the implementation and the
-    // stubs libraries.
-    libs: ["framework-annotations-lib"],
-
-    // Framework modules are not generally shared libraries, i.e. they are not
-    // intended, and must not be allowed, to be used in a <uses-library> manifest
-    // entry.
-    shared_library: false,
-
-    // Prevent dependencies that do not specify an sdk_version from accessing the
-    // implementation library by default and force them to use stubs instead.
-    default_to_stubs: true,
-
-    // Enable api lint. This will eventually become the default for java_sdk_library
-    // but it cannot yet be turned on because some usages have not been cleaned up.
-    // TODO(b/156126315) - Remove when no longer needed.
-    api_lint: {
-        enabled: true,
-    },
-
-    // The API scope specific properties.
-    public: {
-        enabled: true,
-        sdk_version: "module_current",
-    },
-
-    // installable implies we'll create a non-apex (platform) variant, which
-    // we shouldn't ordinarily need (and it can create issues), so disable that.
-    installable: false,
-
-    // Configure framework module specific metalava options.
-    droiddoc_options: [mainline_stubs_args],
-
-    annotations_enabled: true,
-
-    // Allow access to the stubs from anywhere
-    visibility: ["//visibility:public"],
-    stubs_library_visibility: ["//visibility:public"],
-
-    // Hide impl library and stub sources
-    impl_library_visibility: [
-        ":__pkg__",
-        "//frameworks/base", // For framework-all
-    ],
-    stubs_source_visibility: ["//visibility:private"],
-
-    defaults_visibility: ["//visibility:private"],
-
-    // Collates API usages from each module for further analysis.
-    plugins: ["java_api_finder"],
-
-    // Mainline modules should only rely on 'module_lib' APIs provided by other modules
-    // and the non updatable parts of the platform.
-    sdk_version: "module_current",
-}
-
-// Defaults for mainline module provided java_sdk_library instances.
-java_defaults {
-    name: "framework-module-defaults",
-    defaults: ["framework-module-common-defaults"],
-
-    system: {
-        enabled: true,
-        sdk_version: "module_current",
-    },
-    module_lib: {
-        enabled: true,
-        sdk_version: "module_current",
-    },
-    defaults_visibility: [
-        ":__subpackages__",
-        "//frameworks/base/libs/hwui",
-        "//frameworks/base/wifi",
-        "//packages/modules:__subpackages__",
-        "//packages/providers/MediaProvider:__subpackages__",
-    ],
-}
-
-// Defaults for mainline module system server provided java_sdk_library instances.
-java_defaults {
-    name: "framework-system-server-module-defaults",
-    defaults: ["framework-module-common-defaults"],
-
-    system_server: {
-        enabled: true,
-        sdk_version: "module_current",
-    },
-    defaults_visibility: [
-        ":__subpackages__",
-        "//packages/modules:__subpackages__",
-    ],
-}
-
-stubs_defaults {
-    name: "service-module-stubs-srcs-defaults",
-    args: mainline_service_stubs_args,
-    installable: false,
-    annotations_enabled: true,
-    merge_annotations_dirs: [
-        "metalava-manual",
-    ],
-    filter_packages: ["com.android."],
-    check_api: {
-        current: {
-            api_file: "api/current.txt",
-            removed_api_file: "api/removed.txt",
-        },
-        api_lint: {
-            enabled: true,
-        },
-    },
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/system-server/api",
-    },
-}
-
-// Empty for now, but a convenient place to add rules for all
-// module java_library system_server stub libs.
-java_defaults {
-    name: "service-module-stubs-defaults",
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/system-server",
-    },
-}
diff --git a/apex/OWNERS b/apex/OWNERS
index bde2bec..b3e81b9 100644
--- a/apex/OWNERS
+++ b/apex/OWNERS
@@ -1,8 +1 @@
-# Mainline modularization team
-
-andreionea@google.com
-dariofreni@google.com
-hansson@google.com
-mathewi@google.com
-pedroql@google.com
-satayev@google.com
+file:platform/packages/modules/common:/OWNERS
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 6eb44a7..930415f 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -593,15 +593,6 @@
     }
 
     /**
-     * @see JobInfo.Builder#setExpedited(boolean)
-     * @deprecated Use {@link #isExpedited()} instead
-     */
-    @Deprecated
-    public boolean isForegroundJob() {
-        return (flags & FLAG_EXPEDITED) != 0;
-    }
-
-    /**
      * @see JobInfo.Builder#setImportantWhileForeground(boolean)
      */
     public boolean isImportantWhileForeground() {
@@ -1503,20 +1494,6 @@
         }
 
         /**
-         * @deprecated Use {@link #setExpedited(boolean)} instead.
-         */
-        @Deprecated
-        @NonNull
-        public Builder setForeground(boolean foreground) {
-            if (foreground) {
-                mFlags |= FLAG_EXPEDITED;
-            } else {
-                mFlags &= (~FLAG_EXPEDITED);
-            }
-            return this;
-        }
-
-        /**
          * Setting this to true indicates that this job is important while the scheduling app
          * is in the foreground or on the temporary whitelist for background restrictions.
          * This means that the system will relax doze restrictions on this job during this time.
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index ab87222..283e933 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -74,6 +74,27 @@
     }
 
     /**
+     * Allow the temp allowlist behavior, plus allow foreground service start from background.
+     */
+    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
+    /**
+     * Only allow the temp allowlist behavior, not allow foreground service start from
+     * background.
+     */
+    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
+
+    /**
+     * The list of temp allowlist types.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "TEMPORARY_ALLOW_TYPE_" }, value = {
+            TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+            TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TempAllowListType {}
+
+    /**
      * @hide
      */
     public PowerWhitelistManager(@NonNull Context context) {
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 4441643..e045b0f 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -16,7 +16,7 @@
 
 package com.android.server;
 
-import android.app.BroadcastOptions;
+import android.os.PowerWhitelistManager.TempAllowListType;
 
 import com.android.server.deviceidle.IDeviceIdleConstraint;
 
@@ -39,12 +39,12 @@
      * allowlist.
      * @param uid
      * @param duration duration in milliseconds
-     * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+     * @param type temp allowlist type defined at {@link TempAllowListType}
      * @param sync
      * @param reason
      */
     void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
-            @BroadcastOptions.TempAllowListType int type, boolean sync,
+            @TempAllowListType int type, boolean sync,
             String reason);
 
     // duration in milliseconds
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index c9427e9..8f7f705 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -22,7 +22,6 @@
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
 import android.app.BroadcastOptions;
-import android.app.BroadcastOptions.TempAllowListType;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -57,6 +56,7 @@
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
+import android.os.PowerWhitelistManager.TempAllowListType;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -3879,7 +3879,7 @@
      * @param adding true to add to temp allowlist, false to remove from temp allowlist.
      * @param durationMs duration in milliseconds to add to temp allowlist, only valid when
      *                   param adding is true.
-     * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+     * @param type temp allowlist type defined at {@link TempAllowListType}
      */
     private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs,
             @TempAllowListType int type) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 6ab1051..18856f7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -62,6 +62,7 @@
             CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms";
     private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000;
 
+    // Try to give higher priority types lower values.
     static final int WORK_TYPE_NONE = 0;
     static final int WORK_TYPE_TOP = 1 << 0;
     static final int WORK_TYPE_BG = 1 << 1;
@@ -520,6 +521,120 @@
         }
     }
 
+    void onJobCompletedLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
+            @WorkType final int workType) {
+        mWorkCountTracker.onJobFinished(workType);
+        mRunningJobs.remove(jobStatus);
+        final List<JobStatus> pendingJobs = mService.mPendingJobs;
+        if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) {
+            updateCounterConfigLocked();
+            // Preemption case needs special care.
+            updateNonRunningPriorities(pendingJobs, false);
+
+            JobStatus highestPriorityJob = null;
+            int highPriWorkType = workType;
+            JobStatus backupJob = null;
+            int backupWorkType = WORK_TYPE_NONE;
+            for (int i = 0; i < pendingJobs.size(); i++) {
+                final JobStatus nextPending = pendingJobs.get(i);
+
+                if (mRunningJobs.contains(nextPending)) {
+                    continue;
+                }
+
+                if (worker.getPreferredUid() != nextPending.getUid()) {
+                    if (backupJob == null) {
+                        int workAsType =
+                                mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+                        if (workAsType != WORK_TYPE_NONE) {
+                            backupJob = nextPending;
+                            backupWorkType = workAsType;
+                        }
+                    }
+                    continue;
+                }
+
+                if (highestPriorityJob == null
+                        || highestPriorityJob.lastEvaluatedPriority
+                        < nextPending.lastEvaluatedPriority) {
+                    highestPriorityJob = nextPending;
+                } else {
+                    continue;
+                }
+
+                // In this path, we pre-empted an existing job. We don't fully care about the
+                // reserved slots. We should just run the highest priority job we can find,
+                // though it would be ideal to use an available WorkType slot instead of
+                // overloading slots.
+                final int workAsType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+                if (workAsType == WORK_TYPE_NONE) {
+                    // Just use the preempted job's work type since this new one is technically
+                    // replacing it anyway.
+                    highPriWorkType = workType;
+                } else {
+                    highPriWorkType = workAsType;
+                }
+            }
+            if (highestPriorityJob != null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Running job " + jobStatus + " as preemption");
+                }
+                mWorkCountTracker.stageJob(highPriWorkType);
+                startJobLocked(worker, highestPriorityJob, highPriWorkType);
+            } else {
+                if (DEBUG) {
+                    Slog.d(TAG, "Couldn't find preemption job for uid " + worker.getPreferredUid());
+                }
+                worker.clearPreferredUid();
+                if (backupJob != null) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Running job " + jobStatus + " instead");
+                    }
+                    mWorkCountTracker.stageJob(backupWorkType);
+                    startJobLocked(worker, backupJob, backupWorkType);
+                }
+            }
+        } else if (pendingJobs.size() > 0) {
+            updateCounterConfigLocked();
+            updateNonRunningPriorities(pendingJobs, false);
+
+            // This slot is now free and we have pending jobs. Start the highest priority job we
+            // find.
+            JobStatus highestPriorityJob = null;
+            int highPriWorkType = workType;
+            for (int i = 0; i < pendingJobs.size(); i++) {
+                final JobStatus nextPending = pendingJobs.get(i);
+
+                if (mRunningJobs.contains(nextPending)) {
+                    continue;
+                }
+
+                final int workAsType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+                if (workAsType == WORK_TYPE_NONE) {
+                    continue;
+                }
+                if (highestPriorityJob == null
+                        || highestPriorityJob.lastEvaluatedPriority
+                        < nextPending.lastEvaluatedPriority) {
+                    highestPriorityJob = nextPending;
+                    highPriWorkType = workAsType;
+                }
+            }
+
+            if (highestPriorityJob != null) {
+                // This slot is free, and we haven't yet hit the limit on
+                // concurrent jobs...  we can just throw the job in to here.
+                if (DEBUG) {
+                    Slog.d(TAG, "About to run job: " + jobStatus);
+                }
+                mWorkCountTracker.stageJob(highPriWorkType);
+                startJobLocked(worker, highestPriorityJob, highPriWorkType);
+            }
+        }
+
+        noteConcurrency();
+    }
+
     @GuardedBy("mLock")
     private String printPendingQueueLocked() {
         StringBuilder s = new StringBuilder("Pending queue: ");
@@ -855,7 +970,25 @@
             if (numRemainingForType < mNumActuallyReservedSlots.get(workType)) {
                 // We've run all jobs for this type. Let another type use it now.
                 mNumActuallyReservedSlots.put(workType, numRemainingForType);
-                mNumUnspecializedRemaining++;
+                int assignWorkType = WORK_TYPE_NONE;
+                for (int i = 0; i < mNumActuallyReservedSlots.size(); ++i) {
+                    int wt = mNumActuallyReservedSlots.keyAt(i);
+                    if (assignWorkType == WORK_TYPE_NONE || wt < assignWorkType) {
+                        // Try to give this slot to the highest priority one within its limits.
+                        int total = mNumRunningJobs.get(wt) + mNumStartingJobs.get(wt)
+                                + mNumPendingJobs.get(wt);
+                        if (mNumActuallyReservedSlots.valueAt(i) < mConfigAbsoluteMaxSlots.get(wt)
+                                && total > mNumActuallyReservedSlots.valueAt(i)) {
+                            assignWorkType = wt;
+                        }
+                    }
+                }
+                if (assignWorkType != WORK_TYPE_NONE) {
+                    mNumActuallyReservedSlots.put(assignWorkType,
+                            mNumActuallyReservedSlots.get(assignWorkType) + 1);
+                } else {
+                    mNumUnspecializedRemaining++;
+                }
             }
         }
 
@@ -871,6 +1004,18 @@
             }
         }
 
+        void onJobFinished(@WorkType int workType) {
+            final int newNumRunningJobs = mNumRunningJobs.get(workType) - 1;
+            if (newNumRunningJobs < 0) {
+                // We are in a bad state. We will eventually recover when the pending list is
+                // regenerated.
+                Slog.e(TAG, "# running jobs for " + workType + " went negative.");
+                return;
+            }
+            mNumRunningJobs.put(workType, newNumRunningJobs);
+            maybeAdjustReservations(workType);
+        }
+
         void onCountDone() {
             // Calculate how many slots to reserve for each work type. "Unspecialized" slots will
             // be reserved for higher importance types first (ie. top before bg).
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index ba78bda..7ce867c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1426,8 +1426,8 @@
                 // Create the "runners".
                 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
                     mActiveServices.add(
-                            new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
-                                    getContext().getMainLooper()));
+                            new JobServiceContext(this, mConcurrencyManager, mBatteryStats,
+                                    mJobPackageTracker, getContext().getMainLooper()));
                 }
                 // Attach jobs to their controllers.
                 mJobs.forEachJob((job) -> {
@@ -1710,9 +1710,6 @@
             if (DEBUG) {
                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
             }
-            // We still want to check for jobs to execute, because this job may have
-            // scheduled a new job under the same job id, and now we can run it.
-            mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
             return;
         }
 
@@ -1734,7 +1731,6 @@
         }
         jobStatus.unprepareLocked();
         reportActiveLocked();
-        mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
     }
 
     // StateChangedListener implementations.
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 247b421..d15bae0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -107,6 +107,7 @@
     private final Handler mCallbackHandler;
     /** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */
     private final JobCompletedListener mCompletedListener;
+    private final JobConcurrencyManager mJobConcurrencyManager;
     /** Used for service binding, etc. */
     private final Context mContext;
     private final Object mLock;
@@ -183,13 +184,14 @@
         }
     }
 
-    JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats,
-            JobPackageTracker tracker, Looper looper) {
+    JobServiceContext(JobSchedulerService service, JobConcurrencyManager concurrencyManager,
+            IBatteryStats batteryStats, JobPackageTracker tracker, Looper looper) {
         mContext = service.getContext();
         mLock = service.getLock();
         mBatteryStats = batteryStats;
         mJobPackageTracker = tracker;
         mCallbackHandler = new JobServiceHandler(looper);
+        mJobConcurrencyManager = concurrencyManager;
         mCompletedListener = service;
         mAvailable = true;
         mVerb = VERB_FINISHED;
@@ -835,6 +837,7 @@
         if (mWakeLock != null) {
             mWakeLock.release();
         }
+        final int workType = mRunningJobWorkType;
         mContext.unbindService(JobServiceContext.this);
         mWakeLock = null;
         mRunningJob = null;
@@ -847,6 +850,7 @@
         mAvailable = true;
         removeOpTimeOutLocked();
         mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
+        mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType);
     }
 
     private void applyStoppedReasonLocked(String reason) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index e8a2817..d249f2a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -16,7 +16,6 @@
 
 package com.android.server.job.controllers;
 
-import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 
@@ -332,7 +331,7 @@
         if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
             final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps();
             // If we don't know the bandwidth, all we can do is hope the job finishes in time.
-            if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+            if (bandwidth > 0) {
                 // Divide by 8 to convert bits to bytes.
                 final long estimatedMillis = ((downloadBytes * DateUtils.SECOND_IN_MILLIS)
                         / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -350,7 +349,7 @@
         if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
             final long bandwidth = capabilities.getLinkUpstreamBandwidthKbps();
             // If we don't know the bandwidth, all we can do is hope the job finishes in time.
-            if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+            if (bandwidth > 0) {
                 // Divide by 8 to convert bits to bytes.
                 final long estimatedMillis = ((uploadBytes * DateUtils.SECOND_IN_MILLIS)
                         / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -380,18 +379,16 @@
 
     private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
-        final NetworkCapabilities required;
         // A restricted job that's out of quota MUST use an unmetered network.
         if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
                 && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
-            required = new NetworkCapabilities(
+            final NetworkCapabilities required = new NetworkCapabilities.Builder(
                     jobStatus.getJob().getRequiredNetwork().networkCapabilities)
-                    .addCapability(NET_CAPABILITY_NOT_METERED);
+                    .addCapability(NET_CAPABILITY_NOT_METERED).build();
+            return required.satisfiedByNetworkCapabilities(capabilities);
         } else {
-            required = jobStatus.getJob().getRequiredNetwork().networkCapabilities;
+            return jobStatus.getJob().getRequiredNetwork().canBeSatisfiedBy(capabilities);
         }
-
-        return required.satisfiedByNetworkCapabilities(capabilities);
     }
 
     private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
@@ -402,9 +399,9 @@
         }
 
         // See if we match after relaxing any unmetered request
-        final NetworkCapabilities relaxed = new NetworkCapabilities(
+        final NetworkCapabilities relaxed = new NetworkCapabilities.Builder(
                 jobStatus.getJob().getRequiredNetwork().networkCapabilities)
-                        .removeCapability(NET_CAPABILITY_NOT_METERED);
+                        .removeCapability(NET_CAPABILITY_NOT_METERED).build();
         if (relaxed.satisfiedByNetworkCapabilities(capabilities)) {
             // TODO: treat this as "maybe" response; need to check quotas
             return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC;
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 5f13a5c..f85e30d 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -70,6 +70,7 @@
     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_RESET_CAR_MODE = "reset-car-mode";
 
     /**
      * Change the system dialer package name if a package name was specified,
@@ -220,6 +221,9 @@
             case COMMAND_CLEANUP_STUCK_CALLS:
                 runCleanupStuckCalls();
                 break;
+            case COMMAND_RESET_CAR_MODE:
+                runResetCarMode();
+                break;
             case COMMAND_SET_DEFAULT_DIALER:
                 runSetDefaultDialer();
                 break;
@@ -345,6 +349,10 @@
         mTelecomService.cleanupStuckCalls();
     }
 
+    private void runResetCarMode() throws RemoteException {
+        mTelecomService.resetCarMode();
+    }
+
     private void runSetDefaultDialer() throws RemoteException {
         String packageName = nextArg();
         if ("default".equals(packageName)) packageName = null;
diff --git a/core/api/current.txt b/core/api/current.txt
index 5a24251..bfdfb40 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -657,7 +657,7 @@
     field @Deprecated public static final int fontProviderCerts = 16844125; // 0x101055d
     field @Deprecated public static final int fontProviderPackage = 16844119; // 0x1010557
     field @Deprecated public static final int fontProviderQuery = 16844113; // 0x1010551
-    field public static final int fontProviderSystemFontFamily = 16844321; // 0x1010621
+    field public static final int fontProviderSystemFontFamily = 16844322; // 0x1010622
     field public static final int fontStyle = 16844095; // 0x101053f
     field public static final int fontVariationSettings = 16844144; // 0x1010570
     field public static final int fontWeight = 16844083; // 0x1010533
@@ -722,7 +722,7 @@
     field public static final int gwpAsanMode = 16844310; // 0x1010616
     field public static final int hand_hour = 16843011; // 0x1010103
     field public static final int hand_minute = 16843012; // 0x1010104
-    field public static final int hand_second = 16844322; // 0x1010622
+    field public static final int hand_second = 16844323; // 0x1010623
     field public static final int handle = 16843354; // 0x101025a
     field public static final int handleProfiling = 16842786; // 0x1010022
     field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
@@ -964,6 +964,7 @@
     field public static final int measureWithLargestChild = 16843476; // 0x10102d4
     field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad
     field public static final int mediaRouteTypes = 16843694; // 0x10103ae
+    field public static final int memtagMode = 16844324; // 0x1010624
     field public static final int menuCategory = 16843230; // 0x10101de
     field public static final int mimeGroup = 16844309; // 0x1010615
     field public static final int mimeType = 16842790; // 0x1010026
@@ -987,6 +988,7 @@
     field public static final int multiArch = 16843918; // 0x101048e
     field public static final int multiprocess = 16842771; // 0x1010013
     field public static final int name = 16842755; // 0x1010003
+    field public static final int nativeHeapZeroInit = 16844325; // 0x1010625
     field public static final int navigationBarColor = 16843858; // 0x1010452
     field public static final int navigationBarDividerColor = 16844141; // 0x101056d
     field public static final int navigationContentDescription = 16843969; // 0x10104c1
@@ -1057,11 +1059,11 @@
     field public static final int parentActivityName = 16843687; // 0x10103a7
     field @Deprecated public static final int password = 16843100; // 0x101015c
     field public static final int path = 16842794; // 0x101002a
-    field public static final int pathAdvancedPattern = 16844319; // 0x101061f
+    field public static final int pathAdvancedPattern = 16844320; // 0x1010620
     field public static final int pathData = 16843781; // 0x1010405
     field public static final int pathPattern = 16842796; // 0x101002c
     field public static final int pathPrefix = 16842795; // 0x101002b
-    field public static final int pathSuffix = 16844317; // 0x101061d
+    field public static final int pathSuffix = 16844318; // 0x101061e
     field public static final int patternPathData = 16843978; // 0x10104ca
     field public static final int permission = 16842758; // 0x1010006
     field public static final int permissionFlags = 16843719; // 0x10103c7
@@ -1154,7 +1156,7 @@
     field public static final int reqNavigation = 16843306; // 0x101022a
     field public static final int reqTouchScreen = 16843303; // 0x1010227
     field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
-    field public static final int requireDeviceScreenOn = 16844316; // 0x101061c
+    field public static final int requireDeviceScreenOn = 16844317; // 0x101061d
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
     field public static final int requiredAccountType = 16843734; // 0x10103d6
@@ -1295,10 +1297,10 @@
     field public static final int spotShadowAlpha = 16843967; // 0x10104bf
     field public static final int src = 16843033; // 0x1010119
     field public static final int ssp = 16843747; // 0x10103e3
-    field public static final int sspAdvancedPattern = 16844320; // 0x1010620
+    field public static final int sspAdvancedPattern = 16844321; // 0x1010621
     field public static final int sspPattern = 16843749; // 0x10103e5
     field public static final int sspPrefix = 16843748; // 0x10103e4
-    field public static final int sspSuffix = 16844318; // 0x101061e
+    field public static final int sspSuffix = 16844319; // 0x101061f
     field public static final int stackFromBottom = 16843005; // 0x10100fd
     field public static final int stackViewStyle = 16843838; // 0x101043e
     field public static final int starStyle = 16842882; // 0x1010082
@@ -1609,6 +1611,8 @@
     field public static final int windowAnimationStyle = 16842926; // 0x10100ae
     field public static final int windowBackground = 16842836; // 0x1010054
     field public static final int windowBackgroundFallback = 16844035; // 0x1010503
+    field public static final int windowBlurBehindEnabled = 16844316; // 0x101061c
+    field public static final int windowBlurBehindRadius = 16844315; // 0x101061b
     field public static final int windowClipToOutline = 16843947; // 0x10104ab
     field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
     field public static final int windowContentOverlay = 16842841; // 0x1010059
@@ -7312,6 +7316,7 @@
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+    field public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1; // 0x1
     field public static final int WIPE_EUICC = 4; // 0x4
     field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -7465,6 +7470,7 @@
 
   public final class UnsafeStateException extends java.lang.IllegalStateException implements android.os.Parcelable {
     method public int describeContents();
+    method public int getReason();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UnsafeStateException> CREATOR;
   }
@@ -7743,7 +7749,6 @@
     method public long getTriggerContentUpdateDelay();
     method @Nullable public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
     method public boolean isExpedited();
-    method @Deprecated public boolean isForegroundJob();
     method public boolean isImportantWhileForeground();
     method public boolean isPeriodic();
     method public boolean isPersisted();
@@ -7776,7 +7781,6 @@
     method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long, long);
     method @NonNull public android.app.job.JobInfo.Builder setExpedited(boolean);
     method public android.app.job.JobInfo.Builder setExtras(@NonNull android.os.PersistableBundle);
-    method @Deprecated @NonNull public android.app.job.JobInfo.Builder setForeground(boolean);
     method @Deprecated public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
     method public android.app.job.JobInfo.Builder setMinimumLatency(long);
     method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
@@ -9280,7 +9284,7 @@
     method public boolean getIncludeTxPowerLevel();
     method public android.util.SparseArray<byte[]> getManufacturerSpecificData();
     method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData();
-    method @Nullable public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
+    method @NonNull public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
     method public java.util.List<android.os.ParcelUuid> getServiceUuids();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertiseData> CREATOR;
@@ -11639,6 +11643,8 @@
     method public void dump(android.util.Printer, String);
     method public static CharSequence getCategoryTitle(android.content.Context, int);
     method public int getGwpAsanMode();
+    method public int getMemtagMode();
+    method @Nullable public Boolean isNativeHeapZeroInit();
     method public boolean isProfileableByShell();
     method public boolean isResourceOverlay();
     method public boolean isVirtualPreload();
@@ -11688,6 +11694,10 @@
     field public static final int GWP_ASAN_ALWAYS = 1; // 0x1
     field public static final int GWP_ASAN_DEFAULT = -1; // 0xffffffff
     field public static final int GWP_ASAN_NEVER = 0; // 0x0
+    field public static final int MEMTAG_ASYNC = 1; // 0x1
+    field public static final int MEMTAG_DEFAULT = -1; // 0xffffffff
+    field public static final int MEMTAG_OFF = 0; // 0x0
+    field public static final int MEMTAG_SYNC = 2; // 0x2
     field public String appComponentFactory;
     field public String backupAgentName;
     field public int category;
@@ -12288,7 +12298,7 @@
     method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
     method public abstract void removePermission(@NonNull String);
     method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
-    method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
+    method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
     method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
     method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
@@ -12308,7 +12318,6 @@
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
     field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
     field public static final int DONT_KILL_APP = 1; // 0x1
-    field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
     field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
     field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
     field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12494,6 +12503,10 @@
     ctor public PackageManager.NameNotFoundException(String);
   }
 
+  @java.lang.FunctionalInterface public static interface PackageManager.OnChecksumsReadyListener {
+    method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>);
+  }
+
   public static final class PackageManager.Property implements android.os.Parcelable {
     method public int describeContents();
     method public boolean getBoolean();
@@ -15015,6 +15028,7 @@
     field public static final int RGB_565 = 4; // 0x4
     field public static final int UNKNOWN = 0; // 0x0
     field public static final int Y8 = 538982489; // 0x20203859
+    field public static final int YCBCR_P010 = 54; // 0x36
     field public static final int YUV_420_888 = 35; // 0x23
     field public static final int YUV_422_888 = 39; // 0x27
     field public static final int YUV_444_888 = 40; // 0x28
@@ -19453,7 +19467,7 @@
   public interface LocationListener {
     method public default void onFlushComplete(int);
     method public void onLocationChanged(@NonNull android.location.Location);
-    method public default void onLocationChanged(@NonNull android.location.LocationResult);
+    method public default void onLocationChanged(@NonNull java.util.List<android.location.Location>);
     method public default void onProviderDisabled(@NonNull String);
     method public default void onProviderEnabled(@NonNull String);
     method @Deprecated public default void onStatusChanged(String, int, android.os.Bundle);
@@ -19539,8 +19553,8 @@
     field public static final String FUSED_PROVIDER = "fused";
     field public static final String GPS_PROVIDER = "gps";
     field public static final String KEY_FLUSH_COMPLETE = "flushComplete";
+    field public static final String KEY_LOCATIONS = "locations";
     field public static final String KEY_LOCATION_CHANGED = "location";
-    field public static final String KEY_LOCATION_RESULT = "locationResult";
     field public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
     field public static final String KEY_PROXIMITY_ENTERING = "entering";
     field @Deprecated public static final String KEY_STATUS_CHANGED = "status";
@@ -19598,18 +19612,6 @@
     method @NonNull public android.location.LocationRequest.Builder setQuality(int);
   }
 
-  public final class LocationResult implements android.os.Parcelable {
-    method @NonNull public java.util.List<android.location.Location> asList();
-    method @NonNull public static android.location.LocationResult create(@NonNull android.location.Location);
-    method @NonNull public static android.location.LocationResult create(@NonNull java.util.List<android.location.Location>);
-    method public int describeContents();
-    method @NonNull public android.location.Location get(@IntRange(from=0) int);
-    method @NonNull public android.location.Location getLastLocation();
-    method @IntRange(from=1) public int size();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationResult> CREATOR;
-  }
-
   public interface OnNmeaMessageListener {
     method public void onNmeaMessage(String, long);
   }
@@ -20183,7 +20185,7 @@
   }
 
   public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
-    ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
+    ctor @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method @Deprecated public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
     method protected void finalize();
@@ -20244,7 +20246,7 @@
 
   public static class AudioRecord.Builder {
     ctor public AudioRecord.Builder();
-    method public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException;
+    method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException;
     method public android.media.AudioRecord.Builder setAudioFormat(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException;
     method @NonNull public android.media.AudioRecord.Builder setAudioPlaybackCaptureConfig(@NonNull android.media.AudioPlaybackCaptureConfiguration);
     method public android.media.AudioRecord.Builder setAudioSource(int) throws java.lang.IllegalArgumentException;
@@ -31446,6 +31448,7 @@
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
     method public boolean hasUserRestriction(String);
     method public boolean isDemoUser();
+    method public static boolean isHeadlessSystemUserMode();
     method public boolean isManagedProfile();
     method public boolean isQuietModeEnabled(android.os.UserHandle);
     method public boolean isSystemUser();
@@ -34977,6 +34980,55 @@
     field public static final String PATH_SETTING_INTENT = "intent";
   }
 
+  public final class SimPhonebookContract {
+    field public static final String AUTHORITY = "com.android.simphonebook";
+    field @NonNull public static final android.net.Uri AUTHORITY_URI;
+  }
+
+  public static final class SimPhonebookContract.ElementaryFiles {
+    method @NonNull public static android.net.Uri getItemUri(int, int);
+    field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-elementary-file";
+    field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
+    field @NonNull public static final android.net.Uri CONTENT_URI;
+    field public static final int EF_ADN = 1; // 0x1
+    field public static final int EF_FDN = 2; // 0x2
+    field public static final int EF_SDN = 3; // 0x3
+    field public static final String EF_TYPE = "ef_type";
+    field public static final int EF_UNKNOWN = 0; // 0x0
+    field public static final String MAX_RECORDS = "max_records";
+    field public static final String NAME_MAX_LENGTH = "name_max_length";
+    field public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
+    field public static final String RECORD_COUNT = "record_count";
+    field public static final String SLOT_INDEX = "slot_index";
+    field public static final String SUBSCRIPTION_ID = "subscription_id";
+  }
+
+  public static final class SimPhonebookContract.SimRecords {
+    method @NonNull public static android.net.Uri getContentUri(int, int);
+    method @NonNull public static android.net.Uri getItemUri(int, int, int);
+    method @NonNull @WorkerThread public static android.provider.SimPhonebookContract.SimRecords.NameValidationResult validateName(@NonNull android.content.ContentResolver, int, int, @NonNull String);
+    field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
+    field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
+    field public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
+    field public static final String NAME = "name";
+    field public static final String PHONE_NUMBER = "phone_number";
+    field public static final String RECORD_NUMBER = "record_number";
+    field public static final String SUBSCRIPTION_ID = "subscription_id";
+  }
+
+  public static final class SimPhonebookContract.SimRecords.NameValidationResult implements android.os.Parcelable {
+    ctor public SimPhonebookContract.SimRecords.NameValidationResult(@NonNull String, @NonNull String, int, int);
+    method public int describeContents();
+    method public int getEncodedLength();
+    method public int getMaxEncodedLength();
+    method @NonNull public String getName();
+    method @NonNull public String getSanitizedName();
+    method public boolean isSupportedCharacter(int);
+    method public boolean isValid();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.provider.SimPhonebookContract.SimRecords.NameValidationResult> CREATOR;
+  }
+
   public class SyncStateContract {
     ctor public SyncStateContract();
   }
@@ -40343,6 +40395,7 @@
     field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
     field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
     field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
+    field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int";
     field public static final String KEY_PREFIX = "ims.";
     field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
     field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
@@ -45742,6 +45795,8 @@
     method public void clear();
     method public android.util.SparseArray<E> clone();
     method public boolean contains(int);
+    method public boolean contentEquals(@Nullable android.util.SparseArray<E>);
+    method public int contentHashCode();
     method public void delete(int);
     method public E get(int);
     method public E get(int, E);
@@ -49617,7 +49672,7 @@
     field public static final int FLAGS_CHANGED = 4; // 0x4
     field public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 1; // 0x1
     field public static final int FLAG_ALT_FOCUSABLE_IM = 131072; // 0x20000
-    field @Deprecated public static final int FLAG_BLUR_BEHIND = 4; // 0x4
+    field public static final int FLAG_BLUR_BEHIND = 4; // 0x4
     field public static final int FLAG_DIM_BEHIND = 2; // 0x2
     field @Deprecated public static final int FLAG_DISMISS_KEYGUARD = 4194304; // 0x400000
     field @Deprecated public static final int FLAG_DITHER = 4096; // 0x1000
@@ -49708,6 +49763,7 @@
     field @Deprecated public static final int TYPE_TOAST = 2005; // 0x7d5
     field public static final int TYPE_WALLPAPER = 2013; // 0x7dd
     field public float alpha;
+    field public int blurBehindRadius;
     field public float buttonBrightness;
     field public float dimAmount;
     field public int flags;
@@ -53293,7 +53349,7 @@
     method public void onSelectedDayChange(@NonNull android.widget.CalendarView, int, int, int);
   }
 
-  public class CheckBox extends android.widget.CompoundButton {
+  @android.widget.RemoteViews.RemoteView public class CheckBox extends android.widget.CompoundButton {
     ctor public CheckBox(android.content.Context);
     ctor public CheckBox(android.content.Context, android.util.AttributeSet);
     ctor public CheckBox(android.content.Context, android.util.AttributeSet, int);
@@ -53359,6 +53415,7 @@
     method public boolean isChecked();
     method public void setButtonDrawable(@DrawableRes int);
     method public void setButtonDrawable(@Nullable android.graphics.drawable.Drawable);
+    method public void setButtonIcon(@Nullable android.graphics.drawable.Icon);
     method public void setButtonTintBlendMode(@Nullable android.graphics.BlendMode);
     method public void setButtonTintList(@Nullable android.content.res.ColorStateList);
     method public void setButtonTintMode(@Nullable android.graphics.PorterDuff.Mode);
@@ -54399,14 +54456,14 @@
     field protected String[] mExcludeMimes;
   }
 
-  public class RadioButton extends android.widget.CompoundButton {
+  @android.widget.RemoteViews.RemoteView public class RadioButton extends android.widget.CompoundButton {
     ctor public RadioButton(android.content.Context);
     ctor public RadioButton(android.content.Context, android.util.AttributeSet);
     ctor public RadioButton(android.content.Context, android.util.AttributeSet, int);
     ctor public RadioButton(android.content.Context, android.util.AttributeSet, int, int);
   }
 
-  public class RadioGroup extends android.widget.LinearLayout {
+  @android.widget.RemoteViews.RemoteView public class RadioGroup extends android.widget.LinearLayout {
     ctor public RadioGroup(android.content.Context);
     ctor public RadioGroup(android.content.Context, android.util.AttributeSet);
     method public void check(@IdRes int);
@@ -54528,6 +54585,7 @@
     method public void setChronometerCountDown(@IdRes int, boolean);
     method public void setColor(@IdRes int, @NonNull String, @ColorRes int);
     method public void setColorStateList(@IdRes int, @NonNull String, @ColorRes int);
+    method public void setCompoundButtonChecked(@IdRes int, boolean);
     method public void setContentDescription(@IdRes int, CharSequence);
     method public void setDisplayedChild(@IdRes int, int);
     method public void setDouble(@IdRes int, String, double);
@@ -54552,6 +54610,7 @@
     method public void setOnClickResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse);
     method public void setPendingIntentTemplate(@IdRes int, android.app.PendingIntent);
     method public void setProgressBar(@IdRes int, int, int, boolean);
+    method public void setRadioGroupChecked(@IdRes int, @IdRes int);
     method public void setRelativeScrollPosition(@IdRes int, int);
     method @Deprecated public void setRemoteAdapter(int, @IdRes int, android.content.Intent);
     method public void setRemoteAdapter(@IdRes int, android.content.Intent);
@@ -54918,7 +54977,7 @@
     ctor public StackView(android.content.Context, android.util.AttributeSet, int, int);
   }
 
-  public class Switch extends android.widget.CompoundButton {
+  @android.widget.RemoteViews.RemoteView public class Switch extends android.widget.CompoundButton {
     ctor public Switch(android.content.Context);
     ctor public Switch(android.content.Context, android.util.AttributeSet);
     ctor public Switch(android.content.Context, android.util.AttributeSet, int);
@@ -54949,12 +55008,14 @@
     method public void setTextOff(CharSequence);
     method public void setTextOn(CharSequence);
     method public void setThumbDrawable(android.graphics.drawable.Drawable);
+    method public void setThumbIcon(@Nullable android.graphics.drawable.Icon);
     method public void setThumbResource(@DrawableRes int);
     method public void setThumbTextPadding(int);
     method public void setThumbTintBlendMode(@Nullable android.graphics.BlendMode);
     method public void setThumbTintList(@Nullable android.content.res.ColorStateList);
     method public void setThumbTintMode(@Nullable android.graphics.PorterDuff.Mode);
     method public void setTrackDrawable(android.graphics.drawable.Drawable);
+    method public void setTrackIcon(@Nullable android.graphics.drawable.Icon);
     method public void setTrackResource(@DrawableRes int);
     method public void setTrackTintBlendMode(@Nullable android.graphics.BlendMode);
     method public void setTrackTintList(@Nullable android.content.res.ColorStateList);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index be3c246..bf70803 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -182,6 +182,10 @@
     field public static final int TRANSPORT_TEST = 7; // 0x7
   }
 
+  public final class Proxy {
+    method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
+  }
+
   public final class TcpRepairWindow {
     ctor public TcpRepairWindow(int, int, int, int, int, int);
     field public final int maxWindow;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9bff793..3111b7c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -34,6 +34,7 @@
     field public static final String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
     field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE";
     field public static final String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
+    field public static final String BIND_DOMAIN_VERIFICATION_AGENT = "android.permission.BIND_DOMAIN_VERIFICATION_AGENT";
     field public static final String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
     field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
     field public static final String BIND_GBA_SERVICE = "android.permission.BIND_GBA_SERVICE";
@@ -86,6 +87,7 @@
     field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER";
     field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER";
     field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE";
+    field public static final String DOMAIN_VERIFICATION_AGENT = "android.permission.DOMAIN_VERIFICATION_AGENT";
     field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED";
     field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS";
     field public static final String FORCE_BACK = "android.permission.FORCE_BACK";
@@ -258,6 +260,7 @@
     field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
     field public static final String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
     field public static final String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS";
+    field public static final String UPDATE_DOMAIN_VERIFICATION_USER_SELECTION = "android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION";
     field public static final String UPDATE_FONTS = "android.permission.UPDATE_FONTS";
     field public static final String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
     field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
@@ -287,6 +290,7 @@
 
   public static final class R.attr {
     field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+    field public static final int hotwordDetectionService = 16844326; // 0x1010626
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int minExtensionVersion = 16844305; // 0x1010611
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
@@ -629,8 +633,6 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long);
     method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(int, long);
     method public android.os.Bundle toBundle();
-    field public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
-    field public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
   }
 
   public class DownloadManager {
@@ -2102,6 +2104,7 @@
     field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
+    field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
     field public static final String ETHERNET_SERVICE = "ethernet";
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
     field public static final String FONT_SERVICE = "font";
@@ -2145,12 +2148,13 @@
     field public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
     field public static final String ACTION_DEVICE_CUSTOMIZATION_READY = "android.intent.action.DEVICE_CUSTOMIZATION_READY";
     field public static final String ACTION_DIAL_EMERGENCY = "android.intent.action.DIAL_EMERGENCY";
+    field public static final String ACTION_DOMAINS_NEED_VERIFICATION = "android.intent.action.DOMAINS_NEED_VERIFICATION";
     field public static final String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
     field public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
     field public static final String ACTION_INCIDENT_REPORT_READY = "android.intent.action.INCIDENT_REPORT_READY";
     field public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
     field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
-    field public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+    field @Deprecated public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
     field public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
     field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
     field public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
@@ -2483,8 +2487,8 @@
     method @Nullable public abstract android.content.ComponentName getInstantAppInstallerComponent();
     method @Nullable public abstract android.content.ComponentName getInstantAppResolverSettingsComponent();
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_INSTANT_APPS) public abstract java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
-    method @NonNull public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int);
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(@NonNull String);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int);
     method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]);
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
@@ -2508,9 +2512,9 @@
     method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
     method public void setSystemAppState(@NonNull String, int);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
-    method @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int);
     method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle);
-    method @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List<java.lang.String>);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List<java.lang.String>);
     field public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
     field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
     field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
@@ -2520,6 +2524,7 @@
     field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
     field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
     field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
+    field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
     field public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = -268435456; // 0xf0000000
     field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
     field public static final int FLAG_PERMISSION_AUTO_REVOKED = 131072; // 0x20000
@@ -2577,13 +2582,13 @@
     field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99
     field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a
     field public static final int INSTALL_SUCCEEDED = 1; // 0x1
-    field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2; // 0x2
-    field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4; // 0x4
-    field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1; // 0x1
-    field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3; // 0x3
-    field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0; // 0x0
-    field public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; // 0xffffffff
-    field public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
+    field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2; // 0x2
+    field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4; // 0x4
+    field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1; // 0x1
+    field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3; // 0x3
+    field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0; // 0x0
+    field @Deprecated public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; // 0xffffffff
+    field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
     field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
     field public static final int MATCH_ANY_USER = 4194304; // 0x400000
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
@@ -2711,6 +2716,52 @@
 
 }
 
+package android.content.pm.verify.domain {
+
+  public final class DomainVerificationInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getHostToStateMap();
+    method @NonNull public java.util.UUID getIdentifier();
+    method @NonNull public String getPackageName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationInfo> CREATOR;
+  }
+
+  public interface DomainVerificationManager {
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Nullable @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public android.content.pm.verify.domain.DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> getValidVerificationPackageNames();
+    method public static boolean isStateModifiable(int);
+    method public static boolean isStateVerified(int);
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public void setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+    field public static final String EXTRA_VERIFICATION_REQUEST = "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
+    field public static final int STATE_FIRST_VERIFIER_DEFINED = 1024; // 0x400
+    field public static final int STATE_NO_RESPONSE = 0; // 0x0
+    field public static final int STATE_SUCCESS = 1; // 0x1
+  }
+
+  public final class DomainVerificationRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.Set<java.lang.String> getPackageNames();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationRequest> CREATOR;
+  }
+
+  public final class DomainVerificationUserSelection implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.Map<java.lang.String,java.lang.Boolean> getHostToUserSelectionMap();
+    method @NonNull public java.util.UUID getIdentifier();
+    method @NonNull public String getPackageName();
+    method @NonNull public android.os.UserHandle getUser();
+    method @NonNull public boolean isLinkHandlingAllowed();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationUserSelection> CREATOR;
+  }
+
+}
+
 package android.content.rollback {
 
   public final class PackageRollbackInfo implements android.os.Parcelable {
@@ -2860,6 +2911,9 @@
     field public final boolean nightMode;
     field public final String packageName;
     field public final float powerBrightnessFactor;
+    field public final boolean reduceBrightColors;
+    field public final float reduceBrightColorsOffset;
+    field public final int reduceBrightColorsStrength;
     field public final long timeStamp;
   }
 
@@ -4665,10 +4719,6 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
   }
 
-  public final class LocationResult implements android.os.Parcelable {
-    method @NonNull public static android.location.LocationResult wrap(@NonNull android.location.Location);
-  }
-
   public final class SatellitePvt implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.location.SatellitePvt.ClockInfo getClockInfo();
@@ -4735,7 +4785,7 @@
     method public abstract void onSendExtraCommand(@NonNull String, @Nullable android.os.Bundle);
     method public abstract void onSetRequest(@NonNull android.location.provider.ProviderRequest);
     method public void reportLocation(@NonNull android.location.Location);
-    method public void reportLocation(@NonNull android.location.LocationResult);
+    method public void reportLocations(@NonNull java.util.List<android.location.Location>);
     method public void setAllowed(boolean);
     method public void setProperties(@NonNull android.location.provider.ProviderProperties);
     field public static final String ACTION_FUSED_PROVIDER = "com.android.location.service.FusedLocationProvider";
@@ -4938,7 +4988,7 @@
   }
 
   public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
-    ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
+    ctor @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
   }
 
   public static class AudioRecord.Builder {
@@ -8462,6 +8512,8 @@
     field public static final int EVENT_MMS = 2; // 0x2
     field public static final int EVENT_SMS = 1; // 0x1
     field public static final int EVENT_UNSPECIFIED = 0; // 0x0
+    field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
+    field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
   }
 
   public class RecoverySystem {
@@ -9312,6 +9364,24 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean);
   }
 
+  public final class SimPhonebookContract {
+    method @NonNull public static String getEfUriPath(int);
+    field public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
+  }
+
+  public static final class SimPhonebookContract.ElementaryFiles {
+    field public static final String EF_ADN_PATH_SEGMENT = "adn";
+    field public static final String EF_FDN_PATH_SEGMENT = "fdn";
+    field public static final String EF_SDN_PATH_SEGMENT = "sdn";
+    field public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
+  }
+
+  public static final class SimPhonebookContract.SimRecords {
+    field public static final String EXTRA_NAME_VALIDATION_RESULT = "android.provider.extra.NAME_VALIDATION_RESULT";
+    field public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
+    field public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
+  }
+
   public static final class Telephony.Carriers implements android.provider.BaseColumns {
     field public static final String APN_SET_ID = "apn_set_id";
     field public static final int CARRIER_EDITED = 4; // 0x4
@@ -10262,7 +10332,7 @@
     ctor public ExternalStorageService();
     method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
-    method public void onFreeCacheRequested(@NonNull java.util.UUID, long);
+    method public void onFreeCache(@NonNull java.util.UUID, long) throws java.io.IOException;
     method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
     method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException;
     field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
@@ -11635,6 +11705,7 @@
     method public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubscriptionIdList();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public byte[] getAllSimSpecificSettingsForBackup();
     method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
@@ -11642,6 +11713,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
     method public void requestEmbeddedSubscriptionInfoListRefresh();
     method public void requestEmbeddedSubscriptionInfoListRefresh(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
@@ -13727,11 +13799,13 @@
   public static interface RcsCapabilityExchangeImplBase.PublishResponseCallback {
     method public void onCommandError(int) throws android.telephony.ims.ImsException;
     method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+    method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
   }
 
   public static interface RcsCapabilityExchangeImplBase.SubscribeResponseCallback {
     method public void onCommandError(int) throws android.telephony.ims.ImsException;
     method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+    method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
     method public void onNotifyCapabilitiesUpdate(@NonNull java.util.List<java.lang.String>) throws android.telephony.ims.ImsException;
     method public void onResourceTerminated(@NonNull java.util.List<android.util.Pair<android.net.Uri,java.lang.String>>) throws android.telephony.ims.ImsException;
     method public void onTerminated(@NonNull String, long) throws android.telephony.ims.ImsException;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e0391ee..79917d0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -23,10 +23,12 @@
     field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
     field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
     field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS";
+    field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
     field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
     field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
+    field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
     field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
     field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
@@ -118,6 +120,7 @@
 
   public class ActivityOptions {
     method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
+    method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
     method public static void setExitTransitionTimeout(long);
     method public void setLaunchActivityType(int);
     method public void setLaunchTaskId(int);
@@ -389,10 +392,10 @@
     method public boolean isFactoryResetProtectionPolicySupported();
     method @NonNull public static String operationToString(int);
     method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
-    method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, boolean);
+    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
+    method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
+    method @NonNull public static String unsafeOperationReasonToString(int);
     field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
-    field public static final String ACTION_MANAGED_PROFILE_CREATED = "android.app.action.MANAGED_PROFILE_CREATED";
-    field public static final String ACTION_PROVISIONED_MANAGED_DEVICE = "android.app.action.PROVISIONED_MANAGED_DEVICE";
     field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
     field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
     field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
@@ -456,6 +459,7 @@
     field public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
     field public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7; // 0x7
     field public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5; // 0x5
+    field public static final int UNSAFE_OPERATION_REASON_NONE = -1; // 0xffffffff
   }
 
   public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
@@ -512,6 +516,7 @@
   }
 
   public final class UnsafeStateException extends java.lang.IllegalStateException implements android.os.Parcelable {
+    ctor public UnsafeStateException(int, int);
     method public int getOperation();
   }
 
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7402690..9b6f4b4 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -30,6 +30,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.PowerWhitelistManager.TempAllowListType;
 import android.os.TransactionTooLargeException;
 import android.os.WorkSource;
 import android.util.ArraySet;
@@ -103,7 +104,7 @@
      * @param target
      * @param whitelistToken
      * @param duration temp allowlist duration in milliseconds.
-     * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+     * @param type temp allowlist type defined at {@link TempAllowListType}
      */
     public abstract void setPendingIntentWhitelistDuration(IIntentSender target,
             IBinder whitelistToken, long duration, int type);
@@ -136,10 +137,10 @@
      * @param changingUid uid to add or remove to temp allowlist.
      * @param adding true to add to temp allowlist, false to remove from temp allowlist.
      * @param durationMs when adding is true, the duration to be in temp allowlist.
-     * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}.
+     * @param type temp allowlist type defined at {@link TempAllowListType}.
      */
     public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingUid,
-            boolean adding, long durationMs, @BroadcastOptions.TempAllowListType int type);
+            boolean adding, long durationMs, @TempAllowListType int type);
 
     /**
      * Get the procstate for the UID.  The return value will be between
@@ -333,7 +334,7 @@
      * @param callerUid the UID that sent the PendingIntent.
      * @param targetUid the UID that is been temp allowlisted.
      * @param duration temp allowlist duration in milliseconds.
-     * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+     * @param type temp allowlist type defined at {@link TempAllowListType}
      * @param tag
      */
     public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 2b5e18d..28da1c3 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
@@ -310,6 +311,9 @@
     private static final String KEY_REMOTE_TRANSITION =
             "android:activity.remoteTransition";
 
+    private static final String KEY_OVERRIDE_TASK_TRANSITION =
+            "android:activity.overrideTaskTransition";
+
     /**
      * @see #setLaunchCookie
      * @hide
@@ -393,6 +397,7 @@
     private RemoteAnimationAdapter mRemoteAnimationAdapter;
     private IBinder mLaunchCookie;
     private IRemoteTransition mRemoteTransition;
+    private boolean mOverrideTaskTransition;
 
     /**
      * Create an ActivityOptions specifying a custom animation to run when
@@ -476,6 +481,40 @@
     }
 
     /**
+     * Create an ActivityOptions specifying a custom animation to run when the activity in the
+     * different task is displayed.
+     *
+     * @param context Who is defining this.  This is the application that the
+     * animation resources will be loaded from.
+     * @param enterResId A resource ID of the animation resource to use for
+     * the incoming activity.  Use 0 for no animation.
+     * @param exitResId A resource ID of the animation resource to use for
+     * the outgoing activity.  Use 0 for no animation.
+     * @param handler If <var>listener</var> is non-null this must be a valid
+     * Handler on which to dispatch the callback; otherwise it should be null.
+     * @param startedListener Optional OnAnimationStartedListener to find out when the
+     * requested animation has started running.  If for some reason the animation
+     * is not executed, the callback will happen immediately.
+     * @param finishedListener Optional OnAnimationFinishedListener when the animation
+     * has finished running.
+     *
+     * @return Returns a new ActivityOptions object that you can use to
+     * supply these options as the options Bundle when starting an activity.
+     * @hide
+     */
+    @RequiresPermission(START_TASKS_FROM_RECENTS)
+    @TestApi
+    public static @NonNull ActivityOptions makeCustomTaskAnimation(@NonNull Context context,
+            int enterResId, int exitResId, @Nullable Handler handler,
+            @Nullable OnAnimationStartedListener startedListener,
+            @Nullable OnAnimationFinishedListener finishedListener) {
+        ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler,
+                startedListener, finishedListener);
+        opts.mOverrideTaskTransition = true;
+        return opts;
+    }
+
+    /**
      * Creates an ActivityOptions specifying a custom animation to run in place on an existing
      * activity.
      *
@@ -1107,6 +1146,7 @@
         mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE);
         mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder(
                 KEY_REMOTE_TRANSITION));
+        mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION);
     }
 
     /**
@@ -1561,6 +1601,12 @@
         return mLaunchCookie;
     }
 
+
+    /** @hide */
+    public boolean getOverrideTaskTransition() {
+        return mOverrideTaskTransition;
+    }
+
     /**
      * Update the current values in this ActivityOptions from those supplied
      * in <var>otherOptions</var>.  Any values
@@ -1789,6 +1835,9 @@
         if (mRemoteTransition != null) {
             b.putBinder(KEY_REMOTE_TRANSITION, mRemoteTransition.asBinder());
         }
+        if (mOverrideTaskTransition) {
+            b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition);
+        }
         return b;
     }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1f9cb64..e5a04c9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -95,7 +95,6 @@
 import android.media.MediaFrameworkPlatformInitializer;
 import android.media.MediaServiceManager;
 import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
 import android.net.Proxy;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -6576,25 +6575,6 @@
         // Pass the current context to HardwareRenderer
         HardwareRenderer.setContextForInit(getSystemContext());
 
-        /**
-         * Initialize the default http proxy in this process for the reasons we set the time zone.
-         */
-        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Setup proxies");
-        final IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-        if (b != null) {
-            // In pre-boot mode (doing initial launch to collect password), not
-            // all system is up.  This includes the connectivity service, so don't
-            // crash if we can't get it.
-            final IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
-            try {
-                Proxy.setHttpProxySystemProperty(service.getProxyForNetwork(null));
-            } catch (RemoteException e) {
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                throw e.rethrowFromSystemServer();
-            }
-        }
-        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
         // Instrumentation info affects the class loader, so load it before
         // setting up the app context.
         final InstrumentationInfo ii;
@@ -6608,6 +6588,23 @@
         updateLocaleListFromAppContext(appContext,
                 mResourcesManager.getConfiguration().getLocales());
 
+        // Initialize the default http proxy in this process.
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Setup proxies");
+        try {
+            // In pre-boot mode (doing initial launch to collect password), not all system is up.
+            // This includes the connectivity service, so trying to obtain ConnectivityManager at
+            // that point would return null. Check whether the ConnectivityService is available, and
+            // avoid crashing with a NullPointerException if it is not.
+            final IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+            if (b != null) {
+                final ConnectivityManager cm =
+                        appContext.getSystemService(ConnectivityManager.class);
+                Proxy.setHttpProxyConfiguration(cm.getDefaultProxy());
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+
         if (!Process.isIsolated()) {
             final int oldMask = StrictMode.allowThreadDiskWritesMask();
             try {
@@ -7522,8 +7519,8 @@
     }
 
     public static void updateHttpProxy(@NonNull Context context) {
-        final ConnectivityManager cm = ConnectivityManager.from(context);
-        Proxy.setHttpProxySystemProperty(cm.getDefaultProxy());
+        final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+        Proxy.setHttpProxyConfiguration(cm.getDefaultProxy());
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b51d4ac..8ac9139 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -39,11 +39,13 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApkChecksum;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ChangedPackages;
 import android.content.pm.Checksum;
 import android.content.pm.ComponentInfo;
 import android.content.pm.FeatureInfo;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageManager;
@@ -880,10 +882,10 @@
     @Override
     public void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
-            @NonNull IntentSender statusReceiver)
+            @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
             throws CertificateEncodingException, NameNotFoundException {
         Objects.requireNonNull(packageName);
-        Objects.requireNonNull(statusReceiver);
+        Objects.requireNonNull(onChecksumsReadyListener);
         Objects.requireNonNull(trustedInstallers);
         try {
             if (trustedInstallers == TRUST_ALL) {
@@ -895,8 +897,17 @@
                         "trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
                                 + "list of certificates.");
             }
+            IOnChecksumsReadyListener onChecksumsReadyListenerDelegate =
+                    new IOnChecksumsReadyListener.Stub() {
+                        @Override
+                        public void onChecksumsReady(List<ApkChecksum> checksums)
+                                throws RemoteException {
+                            onChecksumsReadyListener.onChecksumsReady(checksums);
+                        }
+                    };
             mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
-                    encodeCertificates(trustedInstallers), statusReceiver, getUserId());
+                    encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate,
+                    getUserId());
         } catch (ParcelableException e) {
             e.maybeRethrow(PackageManager.NameNotFoundException.class);
             throw new RuntimeException(e);
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index a16f6a8..445fdd8 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -16,14 +16,12 @@
 
 package android.app;
 
-import android.annotation.IntDef;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.os.Build;
 import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import android.os.PowerWhitelistManager;
+import android.os.PowerWhitelistManager.TempAllowListType;
 
 /**
  * Helper class for building an options Bundle that can be used with
@@ -75,25 +73,21 @@
             "android:broadcast.allowBackgroundActivityStarts";
 
     /**
-     * Allow the temp allowlist behavior, plus allow foreground service start from background.
-     */
-    public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
-    /**
-     * Only allow the temp allowlist behavior, not allow foreground service start from
-     * background.
-     */
-    public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
-
-    /**
-     * The list of temp allowlist types.
      * @hide
+     * @deprecated Use {@link android.os.PowerWhitelistManager#
+     * TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED} instead.
      */
-    @IntDef(flag = true, prefix = { "TEMPORARY_WHITELIST_TYPE_" }, value = {
-            TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
-            TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TempAllowListType {}
+    @Deprecated
+    public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED =
+            PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+    /**
+     * @hide
+     * @deprecated Use {@link android.os.PowerWhitelistManager#
+     * TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} instead.
+     */
+    @Deprecated
+    public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED =
+            PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
 
     public static BroadcastOptions makeBasic() {
         BroadcastOptions opts = new BroadcastOptions();
@@ -125,7 +119,8 @@
             android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
     public void setTemporaryAppWhitelistDuration(long duration) {
         mTemporaryAppWhitelistDuration = duration;
-        mTemporaryAppWhitelistType = TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+        mTemporaryAppWhitelistType =
+                PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
     }
 
     /**
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 9e1c505..a9e28bb 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -145,18 +145,6 @@
     void onTaskSnapshotChanged(int taskId, in TaskSnapshot snapshot);
 
     /**
-     * Called when the resumed activity is in size compatibility mode and its override configuration
-     * is different from the current one of system.
-     *
-     * @param displayId Id of the display where the activity resides.
-     * @param activityToken Token of the size compatibility mode activity. It will be null when
-     *                      switching to a activity that is not in size compatibility mode or the
-     *                      configuration of the activity.
-     * @see com.android.server.wm.ActivityRecord#inSizeCompatMode
-     */
-    void onSizeCompatModeActivityChanged(int displayId, in IBinder activityToken);
-
-    /**
      * Reports that an Activity received a back key press when there were no additional activities
      * on the back stack.
      *
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f8c33b5..7404e53 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -69,6 +69,9 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutManager;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationManagerImpl;
+import android.content.pm.verify.domain.IDomainVerificationManager;
 import android.content.res.Resources;
 import android.content.rollback.RollbackManagerFrameworkInitializer;
 import android.debug.AdbManager;
@@ -1388,6 +1391,20 @@
                     }
                 });
 
+        // TODO(b/159952358): Only register this service for the domain verification agent?
+        registerService(Context.DOMAIN_VERIFICATION_SERVICE, DomainVerificationManager.class,
+                new CachedServiceFetcher<DomainVerificationManager>() {
+                    @Override
+                    public DomainVerificationManager createService(ContextImpl context)
+                            throws ServiceNotFoundException {
+                        IBinder binder = ServiceManager.getServiceOrThrow(
+                                Context.DOMAIN_VERIFICATION_SERVICE);
+                        IDomainVerificationManager service =
+                                IDomainVerificationManager.Stub.asInterface(binder);
+                        return new DomainVerificationManagerImpl(context, service);
+                    }
+                });
+
         sInitializing = true;
         try {
             // Note: the following functions need to be @SystemApis, once they become mainline
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index d1b544d..517ae24 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -21,7 +21,6 @@
 import android.content.ComponentName;
 import android.os.Binder;
 import android.os.Build;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.window.TaskSnapshot;
 
@@ -163,12 +162,6 @@
     }
 
     @Override
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken)
-            throws RemoteException {
-    }
-
-    @Override
     public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)
             throws RemoteException {
     }
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
index 14ed414..cbe2995 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -15,8 +15,7 @@
  */
 package android.app;
 
-import static android.view.WindowManagerGlobal.ADD_OKAY;
-import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
+import static android.view.WindowManagerImpl.createWindowContextWindowManager;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,8 +27,8 @@
 import android.os.RemoteException;
 import android.view.Display;
 import android.view.IWindowManager;
+import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
-import android.view.WindowManagerImpl;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -46,10 +45,10 @@
  */
 @UiContext
 public class WindowContext extends ContextWrapper {
-    private final WindowManagerImpl mWindowManager;
+    private final WindowManager mWindowManager;
     private final IWindowManager mWms;
     private final WindowTokenClient mToken;
-    private boolean mOwnsToken;
+    private boolean mListenerRegistered;
 
     /**
      * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
@@ -86,25 +85,14 @@
 
         mToken.attachContext(this);
 
-        mWindowManager = new WindowManagerImpl(this);
-        mWindowManager.setDefaultToken(mToken);
+        mWindowManager = createWindowContextWindowManager(this);
 
-        int result;
         try {
-            // Register the token with WindowManager. This will also call back with the current
-            // config back to the client.
-            result = mWms.addWindowTokenWithOptions(
-                    mToken, type, getDisplayId(), options, getPackageName());
+            mListenerRegistered = mWms.registerWindowContextListener(mToken, type, getDisplayId(),
+                    options);
         }  catch (RemoteException e) {
-            mOwnsToken = false;
             throw e.rethrowFromSystemServer();
         }
-        if (result == ADD_TOO_MANY_TOKENS) {
-            throw new UnsupportedOperationException("createWindowContext failed! Too many unused "
-                    + "window contexts. Please see Context#createWindowContext documentation for "
-                    + "detail.");
-        }
-        mOwnsToken = result == ADD_OKAY;
         Reference.reachabilityFence(this);
     }
 
@@ -131,10 +119,10 @@
     /** Used for test to invoke because we can't invoke finalize directly. */
     @VisibleForTesting
     public void release() {
-        if (mOwnsToken) {
+        if (mListenerRegistered) {
+            mListenerRegistered = false;
             try {
-                mWms.removeWindowToken(mToken, getDisplayId());
-                mOwnsToken = false;
+                mWms.unregisterWindowContextListener(mToken);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e84d4a5..06fe9d7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1730,8 +1730,12 @@
      * Broadcast action to notify ManagedProvisioning that
      * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has changed.
      * @hide
+     * @deprecated No longer needed as ManagedProvisioning no longer handles
+     * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction changing.
      */
+    // TODO(b/177221010): Remove when Managed Provisioning no longer depend on it.
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_DATA_SHARING_RESTRICTION_CHANGED =
             "android.app.action.DATA_SHARING_RESTRICTION_CHANGED";
 
@@ -2905,6 +2909,35 @@
         return DebugUtils.constantToString(DevicePolicyManager.class, PREFIX_OPERATION, operation);
     }
 
+    private static final String PREFIX_UNSAFE_OPERATION_REASON = "UNSAFE_OPERATION_REASON_";
+
+    /** @hide */
+    @IntDef(prefix = PREFIX_UNSAFE_OPERATION_REASON, value = {
+            UNSAFE_OPERATION_REASON_NONE,
+            UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public static @interface UnsafeOperationReason {
+    }
+
+    /** @hide */
+    @TestApi
+    public static final int UNSAFE_OPERATION_REASON_NONE = -1;
+
+    /**
+     * Indicates that a {@link UnsafeStateException} was thrown because the operation would distract
+     * the driver of the vehicle.
+     */
+    public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1;
+
+    /** @hide */
+    @NonNull
+    @TestApi
+    public static String unsafeOperationReasonToString(@UnsafeOperationReason int reason) {
+        return DebugUtils.constantToString(DevicePolicyManager.class,
+                PREFIX_UNSAFE_OPERATION_REASON, reason);
+    }
+
     /** @hide */
     public void resetNewUserDisclaimer() {
         if (mService != null) {
@@ -5440,26 +5473,6 @@
             "android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER";
 
     /**
-     * Broadcast action: notify managed provisioning that the device has been provisioned.
-     *
-     * @hide
-     */
-    @TestApi
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PROVISIONED_MANAGED_DEVICE =
-            "android.app.action.PROVISIONED_MANAGED_DEVICE";
-
-    /**
-     * Broadcast action: notify managed provisioning that a new managed profile is created.
-     *
-     * @hide
-     */
-    @TestApi
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MANAGED_PROFILE_CREATED =
-            "android.app.action.MANAGED_PROFILE_CREATED";
-
-    /**
      * Widgets are enabled in keyguard
      */
     public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
@@ -13098,10 +13111,11 @@
      */
     @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS)
-    public void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) {
+    public void setNextOperationSafety(@DevicePolicyOperation int operation,
+            @UnsafeOperationReason int reason) {
         if (mService != null) {
             try {
-                mService.setNextOperationSafety(operation, safe);
+                mService.setNextOperationSafety(operation, reason);
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
@@ -13198,7 +13212,8 @@
             return null;
         }
         try {
-            return mService.createAndProvisionManagedProfile(provisioningParams);
+            return mService.createAndProvisionManagedProfile(
+                    provisioningParams, mContext.getPackageName());
         } catch (ServiceSpecificException e) {
             throw new ProvisioningException(e, e.errorCode);
         } catch (RemoteException e) {
@@ -13229,7 +13244,7 @@
             throws ProvisioningException {
         if (mService != null) {
             try {
-                mService.provisionFullyManagedDevice(provisioningParams);
+                mService.provisionFullyManagedDevice(provisioningParams, mContext.getPackageName());
             } catch (ServiceSpecificException e) {
                 throw new ProvisioningException(e, e.errorCode);
             } catch (RemoteException re) {
@@ -13237,4 +13252,23 @@
             }
         }
     }
+
+    /**
+     * Resets the default cross profile intent filters that were set during
+     * {@link #createAndProvisionManagedProfile} between {@code userId} and all it's managed
+     * profiles if any.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+    public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+        if (mService != null) {
+            try {
+                mService.resetDefaultCrossProfileIntentFilters(userId);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicySafetyChecker.java b/core/java/android/app/admin/DevicePolicySafetyChecker.java
index b1a80c5..6c6f2aa 100644
--- a/core/java/android/app/admin/DevicePolicySafetyChecker.java
+++ b/core/java/android/app/admin/DevicePolicySafetyChecker.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -30,14 +31,16 @@
     /**
      * Returns whether the given {@code operation} can be safely executed at the moment.
      */
-    boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation);
+    @UnsafeOperationReason
+    int getUnsafeOperationReason(@DevicePolicyOperation int operation);
 
     /**
      * Returns a new exception for when the given {@code operation} cannot be safely executed.
      */
     @NonNull
-    default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation) {
-        return new UnsafeStateException(operation);
+    default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation,
+            @UnsafeOperationReason int reason) {
+        return new UnsafeStateException(operation, reason);
     }
 
     /**
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 3765a67..cf0b31e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -491,11 +491,13 @@
     void setManagedProfileMaximumTimeOff(in ComponentName admin, long timeoutMs);
     boolean canProfileOwnerResetPasswordWhenLocked(int userId);
 
-    void setNextOperationSafety(int operation, boolean safe);
+    void setNextOperationSafety(int operation, int reason);
 
     String getEnrollmentSpecificId(String callerPackage);
     void setOrganizationIdForUser(in String callerPackage, in String enterpriseId, int userId);
 
-    UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams);
-    void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams);
+    UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
+    void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage);
+
+    void resetDefaultCrossProfileIntentFilters(int userId);
 }
diff --git a/core/java/android/app/admin/UnsafeStateException.java b/core/java/android/app/admin/UnsafeStateException.java
index 9dcaae4..56eeb06 100644
--- a/core/java/android/app/admin/UnsafeStateException.java
+++ b/core/java/android/app/admin/UnsafeStateException.java
@@ -15,15 +15,21 @@
  */
 package android.app.admin;
 
+import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION;
+import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
+
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
 /**
- * Exception thrown when a {@link DevicePolicyManager} operation failed because it was not safe
- * to be executed at that moment.
+ * Exception thrown when a {@link android.app.admin.DevicePolicyManager} operation failed because it
+ * was not safe to be executed at that moment.
  *
  * <p>For example, it can be thrown on
  * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive devices} when the vehicle
@@ -33,12 +39,19 @@
 public final class UnsafeStateException extends IllegalStateException implements Parcelable {
 
     private final @DevicePolicyOperation int mOperation;
+    private final @UnsafeOperationReason int mReason;
 
     /** @hide */
-    public UnsafeStateException(@DevicePolicyOperation int operation) {
+    @TestApi
+    public UnsafeStateException(@DevicePolicyOperation int operation,
+            @UnsafeOperationReason int reason) {
         super();
-
+        Preconditions.checkArgument(reason == UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
+                "invalid reason %d, must be %d (%s)", reason,
+                UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
+                unsafeOperationReasonToString(UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION));
         mOperation = operation;
+        mReason = reason;
     }
 
     /** @hide */
@@ -47,6 +60,22 @@
         return mOperation;
     }
 
+    /**
+     * Gets the reason the operation is unsafe.
+     *
+     * @return currently, only valid reason is
+     * {@link android.app.admin.DevicePolicyManager#UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION}.
+     */
+    public @UnsafeOperationReason int getReason() {
+        return mReason;
+    }
+
+    /** @hide */
+    @Override
+    public String getMessage() {
+        return DevicePolicyManager.unsafeOperationReasonToString(mReason);
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -55,6 +84,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mOperation);
+        dest.writeInt(mReason);
     }
 
     @NonNull
@@ -63,7 +93,7 @@
 
         @Override
         public UnsafeStateException createFromParcel(Parcel source) {
-            return new UnsafeStateException(source.readInt());
+            return new UnsafeStateException(source.readInt(), source.readInt());
         }
 
         @Override
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 587e883..742d05c 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -41,6 +41,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -460,24 +461,40 @@
                                                    Set<PathWithRequiredFlags> excludes,
                                                    Map<String, Set<PathWithRequiredFlags>> includes)
                 throws IOException, XmlPullParserException {
+            verifyTopLevelTag(parser, "full-backup-content");
+
+            parseRules(parser, excludes, includes, Optional.empty());
+
+            logParsingResults(excludes, includes);
+        }
+
+        private void verifyTopLevelTag(XmlPullParser parser, String tag)
+                throws XmlPullParserException, IOException {
             int event = parser.getEventType(); // START_DOCUMENT
             while (event != XmlPullParser.START_TAG) {
                 event = parser.next();
             }
 
-            if (!"full-backup-content".equals(parser.getName())) {
+            if (!tag.equals(parser.getName())) {
                 throw new XmlPullParserException("Xml file didn't start with correct tag" +
-                        " (<full-backup-content>). Found \"" + parser.getName() + "\"");
+                        " (" + tag + " ). Found \"" + parser.getName() + "\"");
             }
 
             if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
                 Log.v(TAG_XML_PARSER, "\n");
                 Log.v(TAG_XML_PARSER, "====================================================");
-                Log.v(TAG_XML_PARSER, "Found valid fullBackupContent; parsing xml resource.");
+                Log.v(TAG_XML_PARSER, "Found valid " + tag + "; parsing xml resource.");
                 Log.v(TAG_XML_PARSER, "====================================================");
                 Log.v(TAG_XML_PARSER, "");
             }
+        }
 
+        private void parseRules(XmlPullParser parser,
+                Set<PathWithRequiredFlags> excludes,
+                Map<String, Set<PathWithRequiredFlags>> includes,
+                Optional<Integer> maybeRequiredFlags)
+                throws IOException, XmlPullParserException {
+            int event;
             while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 switch (event) {
                     case XmlPullParser.START_TAG:
@@ -498,13 +515,7 @@
                             break;
                         }
 
-                        int requiredFlags = 0; // no transport flags are required by default
-                        if (TAG_INCLUDE.equals(parser.getName())) {
-                            // requiredFlags are only supported for <include /> tag, for <exclude />
-                            // we should always leave them as the default = 0
-                            requiredFlags = getRequiredFlagsFromString(
-                                    parser.getAttributeValue(null, "requireFlags"));
-                        }
+                        int requiredFlags = getRequiredFlagsForRule(parser, maybeRequiredFlags);
 
                         // retrieve the include/exclude set we'll be adding this rule to
                         Set<PathWithRequiredFlags> activeSet = parseCurrentTagForDomain(
@@ -542,7 +553,7 @@
 
                         // Special case for sharedpref files (not dirs) also add ".xml" suffix file.
                         if ("sharedpref".equals(domainFromXml) && !canonicalFile.isDirectory() &&
-                            !canonicalFile.getCanonicalPath().endsWith(".xml")) {
+                                !canonicalFile.getCanonicalPath().endsWith(".xml")) {
                             final String canonicalXmlPath =
                                     canonicalFile.getCanonicalPath() + ".xml";
                             activeSet.add(new PathWithRequiredFlags(canonicalXmlPath,
@@ -554,6 +565,10 @@
                         }
                 }
             }
+        }
+
+        private void logParsingResults(Set<PathWithRequiredFlags> excludes,
+                Map<String, Set<PathWithRequiredFlags>> includes) {
             if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
                 Log.v(TAG_XML_PARSER, "\n");
                 Log.v(TAG_XML_PARSER, "Xml resource parsing complete.");
@@ -613,6 +628,24 @@
             return flags;
         }
 
+        private int getRequiredFlagsForRule(XmlPullParser parser,
+                Optional<Integer> maybeRequiredFlags) {
+            if (maybeRequiredFlags.isPresent()) {
+                // This is the new config format where required flags are specified for the whole
+                // section, not per rule.
+                return maybeRequiredFlags.get();
+            }
+
+            if (TAG_INCLUDE.equals(parser.getName())) {
+                // In the legacy config, requiredFlags are only supported for <include /> tag,
+                // for <exclude /> we should always leave them as the default = 0.
+                return getRequiredFlagsFromString(
+                        parser.getAttributeValue(null, "requireFlags"));
+            }
+
+            return 0;
+        }
+
         private Set<PathWithRequiredFlags> parseCurrentTagForDomain(XmlPullParser parser,
                 Set<PathWithRequiredFlags> excludes,
                 Map<String, Set<PathWithRequiredFlags>> includes, String domain)
diff --git a/core/java/android/app/smartspace/SmartspaceAction.java b/core/java/android/app/smartspace/SmartspaceAction.java
index 033cda1..439d851 100644
--- a/core/java/android/app/smartspace/SmartspaceAction.java
+++ b/core/java/android/app/smartspace/SmartspaceAction.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.content.Intent;
@@ -171,6 +172,7 @@
     /**
      * Returns the extra bundle for this object.
      */
+    @SuppressLint("NullableCollection")
     public @Nullable Bundle getExtras() {
         return mExtras;
     }
@@ -334,7 +336,7 @@
          * Sets the extra.
          */
         @NonNull
-        public Builder setExtras(@Nullable Bundle extras) {
+        public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) {
             mExtras = extras;
             return this;
         }
diff --git a/core/java/android/app/smartspace/SmartspaceConfig.java b/core/java/android/app/smartspace/SmartspaceConfig.java
index 1f9cbb5..07d7bf0 100644
--- a/core/java/android/app/smartspace/SmartspaceConfig.java
+++ b/core/java/android/app/smartspace/SmartspaceConfig.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.Bundle;
@@ -98,6 +99,7 @@
     }
 
     @Nullable
+    @SuppressLint("NullableCollection")
     public Bundle getExtras() {
         return mExtras;
     }
@@ -186,7 +188,7 @@
          * Used to send a bundle containing extras for the {@link SmartspaceConfig}.
          */
         @NonNull
-        public Builder setExtras(@NonNull Bundle extras) {
+        public Builder setExtras(@SuppressLint("NullableCollection") @NonNull Bundle extras) {
             this.mExtras = extras;
             return this;
         }
diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java
index 397326c..cec6580 100644
--- a/core/java/android/bluetooth/le/AdvertiseData.java
+++ b/core/java/android/bluetooth/le/AdvertiseData.java
@@ -44,7 +44,7 @@
     @Nullable
     private final List<ParcelUuid> mServiceUuids;
 
-    @Nullable
+    @NonNull
     private final List<ParcelUuid> mServiceSolicitationUuids;
 
     private final SparseArray<byte[]> mManufacturerSpecificData;
@@ -77,7 +77,7 @@
     /**
      * Returns a list of service solicitation UUIDs within the advertisement that we invite to connect.
      */
-    @Nullable
+    @NonNull
     public List<ParcelUuid> getServiceSolicitationUuids() {
         return mServiceSolicitationUuids;
     }
@@ -221,7 +221,7 @@
     public static final class Builder {
         @Nullable
         private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();
-        @Nullable
+        @NonNull
         private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>();
         private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>();
         private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>();
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 083ce96..102c98f 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -55,6 +55,8 @@
         genBuilder = false)
 public final class AssociationRequest implements Parcelable {
 
+    private static final String LOG_TAG = AssociationRequest.class.getSimpleName();
+
     /**
      * Device profile: watch.
      *
@@ -115,13 +117,6 @@
         mDeviceProfilePrivilegesDescription = desc;
     }
 
-    private void onConstructed() {
-        if (mDeviceProfile != null
-                && !Objects.equals(mDeviceProfile, DEVICE_PROFILE_WATCH)) {
-            throw new IllegalArgumentException("Invalid device profile: " + mDeviceProfile);
-        }
-    }
-
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isSingleDevice() {
@@ -252,7 +247,7 @@
         this.mCallingPackage = callingPackage;
         this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
 
-        onConstructed();
+        // onConstructed(); // You can define this method to get a callback
     }
 
     /**
@@ -386,7 +381,7 @@
         this.mCallingPackage = callingPackage;
         this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
 
-        onConstructed();
+        // onConstructed(); // You can define this method to get a callback
     }
 
     @DataClass.Generated.Member
@@ -404,10 +399,10 @@
     };
 
     @DataClass.Generated(
-            time = 1610132130920L,
+            time = 1611692924843L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
-            inputSignatures = "public static final  java.lang.String DEVICE_PROFILE_WATCH\nprivate  boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\npublic  void setCallingPackage(java.lang.String)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\nprivate  void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
+            inputSignatures = "private static final  java.lang.String LOG_TAG\npublic static final  java.lang.String DEVICE_PROFILE_WATCH\nprivate  boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\npublic  void setCallingPackage(java.lang.String)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java
index 80a7b16..0581ed5 100644
--- a/core/java/android/content/AutofillOptions.java
+++ b/core/java/android/content/AutofillOptions.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.os.Parcel;
@@ -62,6 +63,7 @@
      * List of allowlisted activities.
      */
     @Nullable
+    @SuppressLint("NullableCollection")
     public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill;
 
     /**
@@ -73,6 +75,7 @@
      * The disabled Activities of the package. key is component name string, value is when they
      * will be enabled.
      */
+    @SuppressLint("NullableCollection")
     @Nullable
     public ArrayMap<String, Long> disabledActivities;
 
diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java
index ef49e02..c296bb5 100644
--- a/core/java/android/content/ContentCaptureOptions.java
+++ b/core/java/android/content/ContentCaptureOptions.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.os.Parcel;
@@ -73,6 +74,7 @@
      * for all acitivites in the package).
      */
     @Nullable
+    @SuppressLint("NullableCollection")
     public final ArraySet<ComponentName> whitelistedComponents;
 
     /**
@@ -96,6 +98,7 @@
      */
     public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs,
             int textChangeFlushingFrequencyMs, int logHistorySize,
+            @SuppressLint("NullableCollection")
             @Nullable ArraySet<ComponentName> whitelistedComponents) {
         this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs,
                 textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4dc41b2..5d28216 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -75,6 +75,7 @@
 import android.view.DisplayAdjustments;
 import android.view.View;
 import android.view.ViewDebug;
+import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams.WindowType;
 import android.view.autofill.AutofillManager.AutofillClient;
@@ -5451,6 +5452,15 @@
     public static final String GAME_SERVICE = "game";
 
     /**
+     * Use with {@link #getSystemService(String)} to access domain verification service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @SystemApi
+    public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
@@ -6101,18 +6111,19 @@
      *
      * // WindowManager.LayoutParams initialization
      * ...
+     * // The types used in addView and createWindowContext must match.
      * mParams.type = TYPE_APPLICATION_OVERLAY;
      * ...
      *
-     * mWindowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+     * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
      * </pre>
      *
      * <p>
-     * This context's configuration and resources are adjusted to a display area where the windows
-     * with provided type will be added. <b>Note that all windows associated with the same context
-     * will have an affinity and can only be moved together between different displays or areas on a
-     * display.</b> If there is a need to add different window types, or non-associated windows,
-     * separate Contexts should be used.
+     * This context's configuration and resources are adjusted to an area of the display where
+     * the windows with provided type will be added. <b>Note that all windows associated with the
+     * same context will have an affinity and can only be moved together between different displays
+     * or areas on a display.</b> If there is a need to add different window types, or
+     * non-associated windows, separate Contexts should be used.
      * </p>
      * <p>
      * Creating a window context is an expensive operation. Misuse of this API may lead to a huge
@@ -6120,7 +6131,43 @@
      * An approach is to create one window context with specific window type and display and
      * use it everywhere it's needed.
      * </p>
+     * <p>
+     * After {@link Build.VERSION_CODES#S}, window context provides the capability to receive
+     * configuration changes for existing token by overriding the
+     * {@link android.view.WindowManager.LayoutParams#token token} of the
+     * {@link android.view.WindowManager.LayoutParams} passed in
+     * {@link WindowManager#addView(View, LayoutParams)}. This is useful when an application needs
+     * to attach its window to an existing activity for window token sharing use-case.
+     * </p>
+     * <p>
+     * Note that the window context in {@link Build.VERSION_CODES#R} didn't have this
+     * capability. This is a no-op for the window context in {@link Build.VERSION_CODES#R}.
+     * </p>
+     * Below is sample code to <b>attach an existing token to a window context:</b>
+     * <pre class="prettyprint">
+     * final DisplayManager dm = anyContext.getSystemService(DisplayManager.class);
+     * final Display primaryDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+     * final Context windowContext = anyContext.createWindowContext(primaryDisplay,
+     *         TYPE_APPLICATION, null);
      *
+     * // Get an existing token.
+     * final IBinder existingToken = activity.getWindow().getAttributes().token;
+     *
+     * // The types used in addView() and createWindowContext() must match.
+     * final WindowManager.LayoutParams params = new WindowManager.LayoutParams(TYPE_APPLICATION);
+     * params.token = existingToken;
+     *
+     * // After WindowManager#addView(), the server side will extract the provided token from
+     * // LayoutParams#token (existingToken in the sample code), and switch to propagate
+     * // configuration changes from the node associated with the provided token.
+     * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+     * </pre>
+     * <p>
+     * Note that using {@link android.app.Application} or {@link android.app.Service} context for
+     * UI-related queries may result in layout or continuity issues on devices with variable screen
+     * sizes (e.g. foldables) or in multi-window modes, since these non-UI contexts may not reflect
+     * the {@link Configuration} changes for the visual container.
+     * </p>
      * @param type Window type in {@link WindowManager.LayoutParams}
      * @param options A bundle used to pass window-related options
      * @return A {@link Context} that can be used to create
@@ -6132,9 +6179,7 @@
      * @see #LAYOUT_INFLATER_SERVICE
      * @see #WALLPAPER_SERVICE
      * @throws UnsupportedOperationException if this {@link Context} does not attach to a display,
-     * such as {@link android.app.Application Application} or {@link android.app.Service Service},
-     * or the current number of window contexts without adding any view by
-     * {@link WindowManager#addView} <b>exceeds five</b>.
+     * such as {@link android.app.Application Application} or {@link android.app.Service Service}.
      */
     @UiContext
     @NonNull
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1752b48..30b2404 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -37,6 +37,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.SuspendDialogInfo;
+import android.content.pm.verify.domain.DomainVerificationManager;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
@@ -2841,10 +2842,28 @@
      * </p>
      *
      * @hide
+     * @deprecated Superseded by domain verification APIs. See {@link DomainVerificationManager}.
+     */
+    @Deprecated
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION =
+            "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+
+
+    /**
+     * Broadcast Action: Sent to the system domain verification agent when an app's domains need
+     * to be verified. The data contains the domains hosts to be verified against.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     *
+     * @hide
      */
     @SystemApi
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+    public static final String ACTION_DOMAINS_NEED_VERIFICATION =
+            "android.intent.action.DOMAINS_NEED_VERIFICATION";
 
     /**
      * Broadcast Action: Resources for a set of packages (which were
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index e32068f..6ec1169 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -38,6 +38,8 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForBoolean;
 import com.android.server.SystemConfig;
 
 import java.lang.annotation.Retention;
@@ -56,6 +58,8 @@
  * &lt;application&gt; tag.
  */
 public class ApplicationInfo extends PackageItemInfo implements Parcelable {
+    private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
+
     /**
      * Default task affinity of all activities in this application. See 
      * {@link ActivityInfo#taskAffinity} for more information.  This comes 
@@ -1336,6 +1340,51 @@
     private @GwpAsanMode int gwpAsanMode;
 
     /**
+     * Default (unspecified) setting of Memtag.
+     */
+    public static final int MEMTAG_DEFAULT = -1;
+
+    /**
+     * Do not enable Memtag in this application or process.
+     */
+    public static final int MEMTAG_OFF = 0;
+
+    /**
+     * Enable Memtag in Async mode in this application or process.
+     */
+    public static final int MEMTAG_ASYNC = 1;
+
+    /**
+     * Enable Memtag in Sync mode in this application or process.
+     */
+    public static final int MEMTAG_SYNC = 2;
+
+    /**
+     * These constants need to match the values of memtagMode in application manifest.
+     * @hide
+     */
+    @IntDef(prefix = {"MEMTAG_"}, value = {
+            MEMTAG_DEFAULT,
+            MEMTAG_OFF,
+            MEMTAG_ASYNC,
+            MEMTAG_SYNC,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MemtagMode {}
+
+    /**
+     * Indicates if the application has requested Memtag to be enabled, disabled, or left
+     * unspecified. Processes can override this setting.
+     */
+    private @MemtagMode int memtagMode;
+
+    /**
+     * Enable automatic zero-initialization of native heap memory allocations.
+     */
+    @Nullable
+    private Boolean nativeHeapZeroInit;
+
+    /**
      * Represents the default policy. The actual policy used will depend on other properties of
      * the application, e.g. the target SDK version.
      * @hide
@@ -1479,6 +1528,12 @@
             if (gwpAsanMode != GWP_ASAN_DEFAULT) {
                 pw.println(prefix + "gwpAsanMode=" + gwpAsanMode);
             }
+            if (memtagMode != MEMTAG_DEFAULT) {
+                pw.println(prefix + "memtagMode=" + memtagMode);
+            }
+            if (nativeHeapZeroInit != null) {
+                pw.println(prefix + "nativeHeapZeroInit=" + nativeHeapZeroInit);
+            }
         }
         super.dumpBack(pw, prefix);
     }
@@ -1580,6 +1635,12 @@
             if (gwpAsanMode != GWP_ASAN_DEFAULT) {
                 proto.write(ApplicationInfoProto.Detail.ENABLE_GWP_ASAN, gwpAsanMode);
             }
+            if (memtagMode != MEMTAG_DEFAULT) {
+                proto.write(ApplicationInfoProto.Detail.ENABLE_MEMTAG, memtagMode);
+            }
+            if (nativeHeapZeroInit != null) {
+                proto.write(ApplicationInfoProto.Detail.NATIVE_HEAP_ZERO_INIT, nativeHeapZeroInit);
+            }
             proto.end(detailToken);
         }
         proto.end(token);
@@ -1690,6 +1751,8 @@
         hiddenUntilInstalled = orig.hiddenUntilInstalled;
         zygotePreloadName = orig.zygotePreloadName;
         gwpAsanMode = orig.gwpAsanMode;
+        memtagMode = orig.memtagMode;
+        nativeHeapZeroInit = orig.nativeHeapZeroInit;
     }
 
     public String toString() {
@@ -1774,6 +1837,8 @@
         dest.writeInt(hiddenUntilInstalled ? 1 : 0);
         dest.writeString8(zygotePreloadName);
         dest.writeInt(gwpAsanMode);
+        dest.writeInt(memtagMode);
+        sForBoolean.parcel(nativeHeapZeroInit, dest, parcelableFlags);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1855,6 +1920,8 @@
         hiddenUntilInstalled = source.readInt() != 0;
         zygotePreloadName = source.readString8();
         gwpAsanMode = source.readInt();
+        memtagMode = source.readInt();
+        nativeHeapZeroInit = sForBoolean.unparcel(source);
     }
 
     /**
@@ -2237,6 +2304,8 @@
     /** {@hide} */ public void setBaseResourcePath(String baseResourcePath) { publicSourceDir = baseResourcePath; }
     /** {@hide} */ public void setSplitResourcePaths(String[] splitResourcePaths) { splitPublicSourceDirs = splitResourcePaths; }
     /** {@hide} */ public void setGwpAsanMode(@GwpAsanMode int value) { gwpAsanMode = value; }
+    /** {@hide} */ public void setMemtagMode(@MemtagMode int value) { memtagMode = value; }
+    /** {@hide} */ public void setNativeHeapZeroInit(@Nullable Boolean value) { nativeHeapZeroInit = value; }
 
     /** {@hide} */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -2250,4 +2319,8 @@
     /** {@hide} */ public String[] getSplitResourcePaths() { return splitPublicSourceDirs; }
     @GwpAsanMode
     public int getGwpAsanMode() { return gwpAsanMode; }
+    @MemtagMode
+    public int getMemtagMode() { return memtagMode; }
+    @Nullable
+    public Boolean isNativeHeapZeroInit() { return nativeHeapZeroInit; }
 }
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
similarity index 70%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to core/java/android/content/pm/IOnChecksumsReadyListener.aidl
index 19b20f2..7963ce1 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
@@ -14,7 +14,14 @@
  * limitations under the License.
  */
 
-package android.graphics.fonts;
+package android.content.pm;
 
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+import android.content.pm.ApkChecksum;
+
+/**
+ * Listener that gets notified when checksums are available.
+ * {@hide}
+ */
+oneway interface IOnChecksumsReadyListener {
+    void onChecksumsReady(in List<ApkChecksum> checksums);
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 34d1003..a46876e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -27,6 +27,7 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
 import android.content.pm.InstallSourceInfo;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDeleteObserver2;
@@ -627,9 +628,13 @@
     void verifyPendingInstall(int id, int verificationCode);
     void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
 
+    /** @deprecated */
     void verifyIntentFilter(int id, int verificationCode, in List<String> failedDomains);
+    /** @deprecated */
     int getIntentVerificationStatus(String packageName, int userId);
+    /** @deprecated */
     boolean updateIntentVerificationStatus(String packageName, int status, int userId);
+    /** @deprecated */
     ParceledListSlice getIntentFilterVerifications(String packageName);
     ParceledListSlice getAllIntentFilters(String packageName);
 
@@ -750,7 +755,7 @@
 
     void notifyPackagesReplacedReceived(in String[] packages);
 
-    void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+    void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
 
     //------------------------------------------------------------------------
     //
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9f79dd0..b95b991b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -47,6 +47,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.pm.verify.domain.DomainVerificationManager;
 import android.content.pm.dex.ArtManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -54,6 +55,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
 import android.os.Build;
 import android.os.Bundle;
@@ -69,6 +71,12 @@
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
 import android.permission.PermissionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.gba.GbaService;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.SipDelegateManager;
 import android.util.AndroidException;
 import android.util.Log;
 
@@ -86,6 +94,7 @@
 import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
 
 /**
  * Class for retrieving various kinds of information related to the application
@@ -2194,8 +2203,10 @@
      * {@link PackageManager#verifyIntentFilter} to indicate that the calling
      * IntentFilter Verifier confirms that the IntentFilter is verified.
      *
+     * @deprecated Use {@link DomainVerificationManager} APIs.
      * @hide
      */
+    @Deprecated
     @SystemApi
     public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1;
 
@@ -2204,16 +2215,20 @@
      * {@link PackageManager#verifyIntentFilter} to indicate that the calling
      * IntentFilter Verifier confirms that the IntentFilter is NOT verified.
      *
+     * @deprecated Use {@link DomainVerificationManager} APIs.
      * @hide
      */
+    @Deprecated
     @SystemApi
     public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1;
 
     /**
      * Internal status code to indicate that an IntentFilter verification result is not specified.
      *
+     * @deprecated Use {@link DomainVerificationManager} APIs.
      * @hide
      */
+    @Deprecated
     @SystemApi
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0;
 
@@ -2223,8 +2238,10 @@
      * will always be prompted the Intent Disambiguation Dialog if there are two
      * or more Intent resolved for the IntentFilter's domain(s).
      *
+     * @deprecated Use {@link DomainVerificationManager} APIs.
      * @hide
      */
+    @Deprecated
     @SystemApi
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1;
 
@@ -2235,8 +2252,10 @@
      * or more resolution of the Intent. The default App for the domain(s)
      * specified in the IntentFilter will also ALWAYS be used.
      *
+     * @deprecated Use {@link DomainVerificationManager} APIs.
      * @hide
      */
+    @Deprecated
     @SystemApi
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2;
 
@@ -2247,8 +2266,10 @@
      * Intent resolved. The default App for the domain(s) specified in the
      * IntentFilter will also NEVER be presented to the User.
      *
+     * @deprecated Use {@link DomainVerificationManager} APIs.
      * @hide
      */
+    @Deprecated
     @SystemApi
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3;
 
@@ -2261,8 +2282,10 @@
      * more than one candidate app, then a disambiguation is *always* presented
      * even if there is another candidate app with the 'always' state.
      *
+     * @deprecated Use {@link DomainVerificationManager} APIs.
      * @hide
      */
+    @Deprecated
     @SystemApi
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4;
 
@@ -2935,6 +2958,37 @@
     public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+     * supports a single IMS registration as defined by carrier networks in the IMS service
+     * implementation using the {@link ImsService} API, {@link GbaService} API, and IRadio 1.6 HAL.
+     * <p>
+     * When set, the device must fully support the following APIs for an application to implement
+     * IMS single registration:
+     * <ul>
+     * <li> Updating RCS provisioning status using the {@link ProvisioningManager} API to supply an
+     * RCC.14 defined XML and notify IMS applications of Auto Configuration Server (ACS) or
+     * proprietary server provisioning updates.</li>
+     * <li>Opening a delegate in the device IMS service to forward SIP traffic to the carrier's
+     * network using the {@link SipDelegateManager} API</li>
+     * <li>Listening to EPS dedicated bearer establishment via the
+     * {@link ConnectivityManager#registerQosCallback}
+     * API to indicate to the application when to start/stop media traffic.</li>
+     * <li>Implementing Generic Bootstrapping Architecture (GBA) and providing the associated
+     * authentication keys to applications
+     * requesting this information via the {@link TelephonyManager#bootstrapAuthenticationRequest}
+     * API</li>
+     * <li>Implementing RCS User Capability Exchange using the {@link RcsUceAdapter} API</li>
+     * </ul>
+     * <p>
+     * This feature should only be defined if {@link #FEATURE_TELEPHONY_IMS} is also defined.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION =
+            "android.hardware.telephony.ims.singlereg";
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device is capable of communicating with
      * other devices via ultra wideband.
@@ -3705,8 +3759,10 @@
      * Passed to an intent filter verifier and is used to call back to
      * {@link #verifyIntentFilter}
      *
+     * @deprecated Use DomainVerificationManager APIs.
      * @hide
      */
+    @Deprecated
     public static final String EXTRA_INTENT_FILTER_VERIFICATION_ID
             = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_ID";
 
@@ -3716,8 +3772,10 @@
      *
      * Usually this is "https"
      *
+     * @deprecated Use DomainVerificationManager APIs.
      * @hide
      */
+    @Deprecated
     public static final String EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME
             = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_URI_SCHEME";
 
@@ -3728,8 +3786,10 @@
      *
      * This is a space delimited list of hosts.
      *
+     * @deprecated Use DomainVerificationManager APIs.
      * @hide
      */
+    @Deprecated
     public static final String EXTRA_INTENT_FILTER_VERIFICATION_HOSTS
             = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_HOSTS";
 
@@ -3739,8 +3799,10 @@
      * from the hosts. Each host response will need to include the package name of APK containing
      * the intent filter.
      *
+     * @deprecated Use DomainVerificationManager APIs.
      * @hide
      */
+    @Deprecated
     public static final String EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME
             = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_PACKAGE_NAME";
 
@@ -3796,13 +3858,6 @@
     public static final String EXTRA_FAILURE_EXISTING_PERMISSION
             = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
 
-    /**
-     * Extra field name for the ID of a package pending verification. Passed to
-     * a package verifier and is used to call back to
-     * @see #requestChecksums
-     */
-    public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
-
    /**
     * Permission flag: The permission is set in its current state
     * by the user and apps can still request it at runtime.
@@ -5514,7 +5569,7 @@
      *
      * @hide
      */
-    @SuppressWarnings("HiddenAbstractMethod")
+    @SuppressWarnings({"HiddenAbstractMethod", "NullableCollection"})
     @TestApi
     public abstract @Nullable String[] getNamesForUids(int[] uids);
 
@@ -6918,8 +6973,10 @@
      * @throws SecurityException if the caller does not have the
      *            INTENT_FILTER_VERIFICATION_AGENT permission.
      *
+     * @deprecated Use {@link DomainVerificationManager} APIs.
      * @hide
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT)
@@ -6944,8 +7001,10 @@
      *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER} or
      *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED}
      *
+     * @deprecated Use {@link DomainVerificationManager} APIs.
      * @hide
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
@@ -6970,8 +7029,18 @@
      *
      * @return true if the status has been set. False otherwise.
      *
+     * @deprecated This API represents a very dangerous behavior where Settings or a system app with
+     * the right permissions can force an application to be verified for all of its declared
+     * domains. This has been removed to prevent unintended usage, and no longer does anything,
+     * always returning false. If a caller truly wishes to grant <i></i>every</i> declared web
+     * domain to an application, use
+     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)},
+     * passing in all of the domains returned inside
+     * {@link DomainVerificationManager#getDomainVerificationUserSelection(String)}.
+     *
      * @hide
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @SystemApi
     @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
@@ -6988,8 +7057,10 @@
      *
      * @return a list of IntentFilterVerificationInfo for a specific package.
      *
+     * @deprecated Use {@link DomainVerificationManager} instead.
      * @hide
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @SystemApi
@@ -8631,9 +8702,20 @@
      */
     public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null);
 
+    /** Listener that gets notified when checksums are available. */
+    @FunctionalInterface
+    public interface OnChecksumsReadyListener {
+        /**
+         * Called when the checksums are available.
+         *
+         * @param checksums array of checksums.
+         */
+        void onChecksumsReady(@NonNull List<ApkChecksum> checksums);
+    }
+
     /**
      * Requesting the checksums for APKs within a package.
-     * The checksums will be returned asynchronously via statusReceiver.
+     * The checksums will be returned asynchronously via onChecksumsReadyListener.
      *
      * By default returns all readily available checksums:
      * - enforced by platform,
@@ -8652,15 +8734,14 @@
      *                          {@link #TRUST_ALL} will return checksums from any installer,
      *                          {@link #TRUST_NONE} disables optimized installer-enforced checksums,
      *                          otherwise the list has to be non-empty list of certificates.
-     * @param statusReceiver called once when the results are available as
-     *                       {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[].
+     * @param onChecksumsReadyListener called once when the results are available.
      * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
      * @throws IllegalArgumentException if the list of trusted installer certificates is empty.
      * @throws NameNotFoundException if a package with the given name cannot be found on the system.
      */
     public void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
-            @NonNull IntentSender statusReceiver)
+            @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
             throws CertificateEncodingException, NameNotFoundException {
         throw new UnsupportedOperationException("requestChecksums not implemented in subclass");
     }
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 9925871..5cc74c0 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -77,8 +77,6 @@
     public boolean virtualPreload;
     public int enabled;
     public String lastDisableAppCaller;
-    public int domainVerificationStatus;
-    public int appLinkGeneration;
     public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
     public int installReason;
     public @PackageManager.UninstallReason int uninstallReason;
@@ -100,8 +98,6 @@
         hidden = false;
         suspended = false;
         enabled = COMPONENT_ENABLED_STATE_DEFAULT;
-        domainVerificationStatus =
-                PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
         installReason = PackageManager.INSTALL_REASON_UNKNOWN;
         uninstallReason = PackageManager.UNINSTALL_REASON_UNKNOWN;
     }
@@ -120,8 +116,6 @@
         virtualPreload = o.virtualPreload;
         enabled = o.enabled;
         lastDisableAppCaller = o.lastDisableAppCaller;
-        domainVerificationStatus = o.domainVerificationStatus;
-        appLinkGeneration = o.appLinkGeneration;
         categoryHint = o.categoryHint;
         installReason = o.installReason;
         uninstallReason = o.uninstallReason;
@@ -416,12 +410,6 @@
                         && !lastDisableAppCaller.equals(oldState.lastDisableAppCaller))) {
             return false;
         }
-        if (domainVerificationStatus != oldState.domainVerificationStatus) {
-            return false;
-        }
-        if (appLinkGeneration != oldState.appLinkGeneration) {
-            return false;
-        }
         if (categoryHint != oldState.categoryHint) {
             return false;
         }
@@ -481,8 +469,6 @@
         hashCode = 31 * hashCode + Boolean.hashCode(virtualPreload);
         hashCode = 31 * hashCode + enabled;
         hashCode = 31 * hashCode + Objects.hashCode(lastDisableAppCaller);
-        hashCode = 31 * hashCode + domainVerificationStatus;
-        hashCode = 31 * hashCode + appLinkGeneration;
         hashCode = 31 * hashCode + categoryHint;
         hashCode = 31 * hashCode + installReason;
         hashCode = 31 * hashCode + uninstallReason;
diff --git a/core/java/android/content/pm/ProcessInfo.java b/core/java/android/content/pm/ProcessInfo.java
index d45ff98..3dd5ee1 100644
--- a/core/java/android/content/pm/ProcessInfo.java
+++ b/core/java/android/content/pm/ProcessInfo.java
@@ -53,16 +53,30 @@
      */
     public @ApplicationInfo.GwpAsanMode int gwpAsanMode;
 
+    /**
+     * Indicates if the process has requested Memtag to be enabled (in sync or async mode),
+     * disabled, or left unspecified.
+     */
+    public @ApplicationInfo.MemtagMode int memtagMode;
+
+    /**
+     * Enable automatic zero-initialization of native heap memory allocations.
+     */
+    @Nullable
+    public Boolean nativeHeapZeroInit;
+
     @Deprecated
     public ProcessInfo(@NonNull ProcessInfo orig) {
         this.name = orig.name;
         this.deniedPermissions = orig.deniedPermissions;
         this.gwpAsanMode = orig.gwpAsanMode;
+        this.memtagMode = orig.memtagMode;
+        this.nativeHeapZeroInit = orig.nativeHeapZeroInit;
     }
 
 
 
-    // Code below generated by codegen v1.0.15.
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -84,12 +98,19 @@
      *   If non-null, these are permissions that are not allowed in this process.
      * @param gwpAsanMode
      *   Indicates if the process has requested GWP-ASan to be enabled, disabled, or left unspecified.
+     * @param memtagMode
+     *   Indicates if the process has requested Memtag to be enabled (in sync or async mode),
+     *   disabled, or left unspecified.
+     * @param nativeHeapZeroInit
+     *   Enable automatic zero-initialization of native heap memory allocations.
      */
     @DataClass.Generated.Member
     public ProcessInfo(
             @NonNull String name,
             @Nullable ArraySet<String> deniedPermissions,
-            @ApplicationInfo.GwpAsanMode int gwpAsanMode) {
+            @ApplicationInfo.GwpAsanMode int gwpAsanMode,
+            @ApplicationInfo.MemtagMode int memtagMode,
+            @Nullable Boolean nativeHeapZeroInit) {
         this.name = name;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, name);
@@ -97,6 +118,10 @@
         this.gwpAsanMode = gwpAsanMode;
         com.android.internal.util.AnnotationValidations.validate(
                 ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+        this.memtagMode = memtagMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.MemtagMode.class, null, memtagMode);
+        this.nativeHeapZeroInit = nativeHeapZeroInit;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -120,10 +145,13 @@
 
         byte flg = 0;
         if (deniedPermissions != null) flg |= 0x2;
+        if (nativeHeapZeroInit != null) flg |= 0x10;
         dest.writeByte(flg);
         dest.writeString(name);
         sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
         dest.writeInt(gwpAsanMode);
+        dest.writeInt(memtagMode);
+        if (nativeHeapZeroInit != null) dest.writeBoolean(nativeHeapZeroInit);
     }
 
     @Override
@@ -141,6 +169,8 @@
         String _name = in.readString();
         ArraySet<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
         int _gwpAsanMode = in.readInt();
+        int _memtagMode = in.readInt();
+        Boolean _nativeHeapZeroInit = (flg & 0x10) == 0 ? null : (Boolean) in.readBoolean();
 
         this.name = _name;
         com.android.internal.util.AnnotationValidations.validate(
@@ -149,6 +179,10 @@
         this.gwpAsanMode = _gwpAsanMode;
         com.android.internal.util.AnnotationValidations.validate(
                 ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+        this.memtagMode = _memtagMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.MemtagMode.class, null, memtagMode);
+        this.nativeHeapZeroInit = _nativeHeapZeroInit;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -168,10 +202,10 @@
     };
 
     @DataClass.Generated(
-            time = 1584555730519L,
-            codegenVersion = "1.0.15",
+            time = 1611614699049L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/ProcessInfo.java",
-            inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+            inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\npublic @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\npublic @android.annotation.Nullable java.lang.Boolean nativeHeapZeroInit\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 8147599..7a01392 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -252,6 +252,10 @@
 
     ParsingPackage setGwpAsanMode(int gwpAsanMode);
 
+    ParsingPackage setMemtagMode(int memtagMode);
+
+    ParsingPackage setNativeHeapZeroInit(@Nullable Boolean nativeHeapZeroInit);
+
     ParsingPackage setCrossProfile(boolean crossProfile);
 
     ParsingPackage setFullBackupContent(int fullBackupContent);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 51ec297..c1a93d8 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -380,6 +380,11 @@
     private int autoRevokePermissions;
 
     protected int gwpAsanMode;
+    protected int memtagMode;
+
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    private Boolean nativeHeapZeroInit;
 
     // TODO(chiuwinson): Non-null
     @Nullable
@@ -1058,6 +1063,8 @@
         appInfo.volumeUuid = volumeUuid;
         appInfo.zygotePreloadName = zygotePreloadName;
         appInfo.setGwpAsanMode(gwpAsanMode);
+        appInfo.setMemtagMode(memtagMode);
+        appInfo.setNativeHeapZeroInit(nativeHeapZeroInit);
         appInfo.setBaseCodePath(mBaseApkPath);
         appInfo.setBaseResourcePath(mBaseApkPath);
         appInfo.setCodePath(mPath);
@@ -1190,6 +1197,8 @@
         dest.writeSparseIntArray(this.minExtensionVersions);
         dest.writeLong(this.mBooleans);
         dest.writeMap(this.mProperties);
+        dest.writeInt(this.memtagMode);
+        sForBoolean.parcel(this.nativeHeapZeroInit, dest, flags);
     }
 
     public ParsingPackageImpl(Parcel in) {
@@ -1310,6 +1319,8 @@
         this.minExtensionVersions = in.readSparseIntArray();
         this.mBooleans = in.readLong();
         this.mProperties = in.createTypedArrayMap(Property.CREATOR);
+        this.memtagMode = in.readInt();
+        this.nativeHeapZeroInit = sForBoolean.unparcel(in);
         assignDerivedFields();
     }
 
@@ -2062,6 +2073,17 @@
     }
 
     @Override
+    public int getMemtagMode() {
+        return memtagMode;
+    }
+
+    @Nullable
+    @Override
+    public Boolean isNativeHeapZeroInit() {
+        return nativeHeapZeroInit;
+    }
+
+    @Override
     public boolean isPartiallyDirectBootAware() {
         return getBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE);
     }
@@ -2493,6 +2515,18 @@
     }
 
     @Override
+    public ParsingPackageImpl setMemtagMode(int value) {
+        memtagMode = value;
+        return this;
+    }
+
+    @Override
+    public ParsingPackageImpl setNativeHeapZeroInit(@Nullable Boolean value) {
+        nativeHeapZeroInit = value;
+        return this;
+    }
+
+    @Override
     public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) {
         return setBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE, value);
     }
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index a102e82..ff4cebd 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -874,6 +874,19 @@
      */
     int getGwpAsanMode();
 
+    /**
+     * @see ApplicationInfo#memtagMode
+     * @see R.styleable#AndroidManifest_memtagMode
+     */
+    int getMemtagMode();
+
+      /**
+     * @see ApplicationInfo#nativeHeapZeroInit
+     * @see R.styleable#AndroidManifest_nativeHeapZeroInit
+     */
+    @Nullable
+    Boolean isNativeHeapZeroInit();
+
     // TODO(b/135203078): Hide and enforce going through PackageInfoUtils
     ApplicationInfo toAppInfoWithoutState();
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 8fbf287..66bdb9b 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -1984,6 +1984,11 @@
             }
 
             pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
+            pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1));
+            if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInit)) {
+                pkg.setNativeHeapZeroInit(sa.getBoolean(
+                        R.styleable.AndroidManifestApplication_nativeHeapZeroInit, false));
+            }
         } finally {
             sa.recycle();
         }
@@ -2493,6 +2498,10 @@
 
     /**
      * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
+     *
+     * This is distinct from any of the functionality of app links domain verification, and cannot
+     * be converted to remain backwards compatible. It's possible the presence of this flag does
+     * not indicate a valid package for domain verification.
      */
     private static boolean hasDomainURLs(ParsingPackage pkg) {
         final List<ParsedActivity> activities = pkg.getActivities();
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index cbd2c55..9012b5ce 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -108,17 +108,14 @@
 
         permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel);
 
-        if (permission.getProtectionFlags() != 0) {
-            if ((permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
-                    && (permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
-                    == 0
-                    && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
-                    != PermissionInfo.PROTECTION_SIGNATURE
-                    && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
-                    != PermissionInfo.PROTECTION_INTERNAL) {
-                return input.error("<permission>  protectionLevel specifies a non-instant flag "
-                        + "but is not based on signature or internal type");
-            }
+        final int otherProtectionFlags = permission.getProtectionFlags()
+                & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT
+                | PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY);
+        if (otherProtectionFlags != 0
+                && permission.getProtection() != PermissionInfo.PROTECTION_SIGNATURE
+                && permission.getProtection() != PermissionInfo.PROTECTION_INTERNAL) {
+            return input.error("<permission> protectionLevel specifies a non-instant, non-appop,"
+                    + " non-runtimeOnly flag but is not based on signature or internal type");
         }
 
         return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
index e0ae81b..89fef9d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
@@ -41,7 +42,10 @@
     @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
     protected Set<String> deniedPermissions = emptySet();
 
-    protected int gwpAsanMode = -1;
+    protected int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
+    protected int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
+    @Nullable
+    protected Boolean nativeHeapZeroInit = null;
 
     public ParsedProcess() {
     }
@@ -57,7 +61,7 @@
 
 
 
-    // Code below generated by codegen v1.0.15.
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -74,7 +78,9 @@
     public ParsedProcess(
             @NonNull String name,
             @NonNull Set<String> deniedPermissions,
-            int gwpAsanMode) {
+            int gwpAsanMode,
+            int memtagMode,
+            @Nullable Boolean nativeHeapZeroInit) {
         this.name = name;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, name);
@@ -82,6 +88,8 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, deniedPermissions);
         this.gwpAsanMode = gwpAsanMode;
+        this.memtagMode = memtagMode;
+        this.nativeHeapZeroInit = nativeHeapZeroInit;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -102,6 +110,16 @@
     }
 
     @DataClass.Generated.Member
+    public int getMemtagMode() {
+        return memtagMode;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable Boolean getNativeHeapZeroInit() {
+        return nativeHeapZeroInit;
+    }
+
+    @DataClass.Generated.Member
     static Parcelling<Set<String>> sParcellingForDeniedPermissions =
             Parcelling.Cache.get(
                     Parcelling.BuiltIn.ForInternedStringSet.class);
@@ -118,9 +136,14 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
+        byte flg = 0;
+        if (nativeHeapZeroInit != null) flg |= 0x10;
+        dest.writeByte(flg);
         dest.writeString(name);
         sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
         dest.writeInt(gwpAsanMode);
+        dest.writeInt(memtagMode);
+        if (nativeHeapZeroInit != null) dest.writeBoolean(nativeHeapZeroInit);
     }
 
     @Override
@@ -134,9 +157,12 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
+        byte flg = in.readByte();
         String _name = in.readString();
         Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
         int _gwpAsanMode = in.readInt();
+        int _memtagMode = in.readInt();
+        Boolean _nativeHeapZeroInit = (flg & 0x10) == 0 ? null : (Boolean) in.readBoolean();
 
         this.name = _name;
         com.android.internal.util.AnnotationValidations.validate(
@@ -145,6 +171,8 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, deniedPermissions);
         this.gwpAsanMode = _gwpAsanMode;
+        this.memtagMode = _memtagMode;
+        this.nativeHeapZeroInit = _nativeHeapZeroInit;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -164,10 +192,10 @@
     };
 
     @DataClass.Generated(
-            time = 1584557524776L,
-            codegenVersion = "1.0.15",
+            time = 1611615591258L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java",
-            inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected  int gwpAsanMode\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+            inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected  int gwpAsanMode\nprotected  int memtagMode\nprotected @android.annotation.Nullable java.lang.Boolean nativeHeapZeroInit\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index 9bff719..2579774 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -103,6 +103,11 @@
             }
 
             proc.gwpAsanMode = sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1);
+            proc.memtagMode = sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1);
+            if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInit)) {
+                proc.nativeHeapZeroInit =
+                        sa.getBoolean(R.styleable.AndroidManifestProcess_nativeHeapZeroInit, false);
+            }
         } finally {
             sa.recycle();
         }
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl
similarity index 80%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl
index 19b20f2..c143cc5 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-package android.graphics.fonts;
+package android.content.pm.verify.domain;
 
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+parcelable DomainVerificationInfo;
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
new file mode 100644
index 0000000..7afbe1f
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.pm.PackageManager;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Contains the state of all domains for a given package on device. Used by the domain verification
+ * agent to determine the domains declared by a package that need to be verified by comparing
+ * against the digital asset links response from the server hosting that domain.
+ * <p>
+ * These values for each domain can be modified through
+ * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}.
+ *
+ * @hide
+ */
+@SystemApi
+@SuppressWarnings("DefaultAnnotationParam")
+@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
+        genEqualsHashCode = true)
+public final class DomainVerificationInfo implements Parcelable {
+
+    /**
+     * A domain verification ID for use in later API calls. This represents the snapshot of the
+     * domains for a package on device, and will be invalidated whenever the package changes.
+     * <p>
+     * An exception will be thrown at the next API call that receives the ID if it is no longer
+     * valid.
+     * <p>
+     * The caller may also be notified with a broadcast whenever a package and ID is invalidated, at
+     * which point it can use the package name to evict existing requests with an invalid set ID. If
+     * the caller wants to manually check if any IDs have been invalidate, the {@link
+     * PackageManager#getChangedPackages(int)} API will allow tracking the packages changed since
+     * the last query of this method, prompting the caller to re-query.
+     * <p>
+     * This allows the caller to arbitrarily grant or revoke domain verification status, through
+     * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}.
+     */
+    @NonNull
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForUUID.class)
+    private final UUID mIdentifier;
+
+    /**
+     * The package name that this data corresponds to.
+     */
+    @NonNull
+    private final String mPackageName;
+
+    /**
+     * Map of host names to their current state. State is an integer, which defaults to
+     * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the
+     * domain verification agent (the intended consumer of this API), which can be equal
+     * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or
+     * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for
+     * any unsuccessful response.
+     * <p>
+     * Any value non-inclusive between those 2 values are reserved for use by the system.
+     * The domain verification agent may be able to act on these reserved values, and this
+     * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}.
+     * It is expected that the agent attempt to verify all domains that it can modify the
+     * state of, even if it does not understand the meaning of those values.
+     */
+    @NonNull
+    private final Map<String, Integer> mHostToStateMap;
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new DomainVerificationInfo.
+     *
+     * @param identifier
+     *   A domain verification ID for use in later API calls. This represents the snapshot of the
+     *   domains for a package on device, and will be invalidated whenever the package changes.
+     *   <p>
+     *   An exception will be thrown at the next API call that receives the ID if it is no longer
+     *   valid.
+     *   <p>
+     *   The caller may also be notified with a broadcast whenever a package and ID is invalidated, at
+     *   which point it can use the package name to evict existing requests with an invalid set ID. If
+     *   the caller wants to manually check if any IDs have been invalidate, the {@link
+     *   PackageManager#getChangedPackages(int)} API will allow tracking the packages changed since
+     *   the last query of this method, prompting the caller to re-query.
+     *   <p>
+     *   This allows the caller to arbitrarily grant or revoke domain verification status, through
+     *   {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}.
+     * @param packageName
+     *   The package name that this data corresponds to.
+     * @param hostToStateMap
+     *   Map of host names to their current state. State is an integer, which defaults to
+     *   {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the
+     *   domain verification agent (the intended consumer of this API), which can be equal
+     *   to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or
+     *   greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for
+     *   any unsuccessful response.
+     *   <p>
+     *   Any value non-inclusive between those 2 values are reserved for use by the system.
+     *   The domain verification agent may be able to act on these reserved values, and this
+     *   ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}.
+     *   It is expected that the agent attempt to verify all domains that it can modify the
+     *   state of, even if it does not understand the meaning of those values.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public DomainVerificationInfo(
+            @NonNull UUID identifier,
+            @NonNull String packageName,
+            @NonNull Map<String,Integer> hostToStateMap) {
+        this.mIdentifier = identifier;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mIdentifier);
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mHostToStateMap = hostToStateMap;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHostToStateMap);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * A domain verification ID for use in later API calls. This represents the snapshot of the
+     * domains for a package on device, and will be invalidated whenever the package changes.
+     * <p>
+     * An exception will be thrown at the next API call that receives the ID if it is no longer
+     * valid.
+     * <p>
+     * The caller may also be notified with a broadcast whenever a package and ID is invalidated, at
+     * which point it can use the package name to evict existing requests with an invalid set ID. If
+     * the caller wants to manually check if any IDs have been invalidate, the {@link
+     * PackageManager#getChangedPackages(int)} API will allow tracking the packages changed since
+     * the last query of this method, prompting the caller to re-query.
+     * <p>
+     * This allows the caller to arbitrarily grant or revoke domain verification status, through
+     * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}.
+     */
+    @DataClass.Generated.Member
+    public @NonNull UUID getIdentifier() {
+        return mIdentifier;
+    }
+
+    /**
+     * The package name that this data corresponds to.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Map of host names to their current state. State is an integer, which defaults to
+     * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the
+     * domain verification agent (the intended consumer of this API), which can be equal
+     * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or
+     * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for
+     * any unsuccessful response.
+     * <p>
+     * Any value non-inclusive between those 2 values are reserved for use by the system.
+     * The domain verification agent may be able to act on these reserved values, and this
+     * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}.
+     * It is expected that the agent attempt to verify all domains that it can modify the
+     * state of, even if it does not understand the meaning of those values.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Map<String,Integer> getHostToStateMap() {
+        return mHostToStateMap;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "DomainVerificationInfo { " +
+                "identifier = " + mIdentifier + ", " +
+                "packageName = " + mPackageName + ", " +
+                "hostToStateMap = " + mHostToStateMap +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(DomainVerificationInfo other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        DomainVerificationInfo that = (DomainVerificationInfo) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mIdentifier, that.mIdentifier)
+                && java.util.Objects.equals(mPackageName, that.mPackageName)
+                && java.util.Objects.equals(mHostToStateMap, that.mHostToStateMap);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mIdentifier);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mHostToStateMap);
+        return _hash;
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<UUID> sParcellingForIdentifier =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForUUID.class);
+    static {
+        if (sParcellingForIdentifier == null) {
+            sParcellingForIdentifier = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForUUID());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        sParcellingForIdentifier.parcel(mIdentifier, dest, flags);
+        dest.writeString(mPackageName);
+        dest.writeMap(mHostToStateMap);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ DomainVerificationInfo(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        UUID identifier = sParcellingForIdentifier.unparcel(in);
+        String packageName = in.readString();
+        Map<String,Integer> hostToStateMap = new java.util.LinkedHashMap<>();
+        in.readMap(hostToStateMap, Integer.class.getClassLoader());
+
+        this.mIdentifier = identifier;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mIdentifier);
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mHostToStateMap = hostToStateMap;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHostToStateMap);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<DomainVerificationInfo> CREATOR
+            = new Parcelable.Creator<DomainVerificationInfo>() {
+        @Override
+        public DomainVerificationInfo[] newArray(int size) {
+            return new DomainVerificationInfo[size];
+        }
+
+        @Override
+        public DomainVerificationInfo createFromParcel(@NonNull android.os.Parcel in) {
+            return new DomainVerificationInfo(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1611862790369L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java",
+            inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
new file mode 100644
index 0000000..af12536
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.IntDef;
+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.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.UserHandle;
+
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * System service to access the domain verification APIs.
+ *
+ * Allows the approved domain verification
+ * agent on the device (the sole holder of
+ * {@link android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}) to update the approval status
+ * of domains declared by applications in their AndroidManifest.xml, to allow them to open those
+ * links inside the app when selected by the user. This is done through querying
+ * {@link #getDomainVerificationInfo(String)} and calling
+ * {@link #setDomainVerificationStatus(UUID, Set, int)}.
+ *
+ * Also allows the domain preference settings (holder of
+ * {@link android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) to update the
+ * preferences of the user, when they have chosen to explicitly allow an application to open links.
+ * This is done through querying {@link #getDomainVerificationUserSelection(String)} and calling
+ * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)} and
+ * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.DOMAIN_VERIFICATION_SERVICE)
+public interface DomainVerificationManager {
+
+    /**
+     * Extra field name for a {@link DomainVerificationRequest} for the requested packages.
+     * Passed to an the domain verification agent that handles
+     * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION}.
+     */
+    String EXTRA_VERIFICATION_REQUEST =
+            "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
+
+    /**
+     * No response has been recorded by either the system or any verification agent.
+     */
+    int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
+
+    /** The verification agent has explicitly verified the domain at some point. */
+    int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
+
+    /**
+     * The first available custom response code. This and any greater integer, along with
+     * {@link #STATE_SUCCESS} are the only values settable by the verification agent. All values
+     * will be treated as if the domain is unverified.
+     */
+    int STATE_FIRST_VERIFIER_DEFINED = DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+
+    /** @hide */
+    @NonNull
+    static String stateToDebugString(@DomainVerificationState.State int state) {
+        switch (state) {
+            case DomainVerificationState.STATE_NO_RESPONSE:
+                return "none";
+            case DomainVerificationState.STATE_SUCCESS:
+                return "verified";
+            case DomainVerificationState.STATE_APPROVED:
+                return "approved";
+            case DomainVerificationState.STATE_DENIED:
+                return "denied";
+            case DomainVerificationState.STATE_MIGRATED:
+                return "migrated";
+            case DomainVerificationState.STATE_RESTORED:
+                return "restored";
+            case DomainVerificationState.STATE_LEGACY_FAILURE:
+                return "legacy_failure";
+            case DomainVerificationState.STATE_SYS_CONFIG:
+                return "system_configured";
+            default:
+                return String.valueOf(state);
+        }
+    }
+
+    /**
+     * Checks if a state considers the corresponding domain to be successfully verified. The
+     * domain verification agent may use this to determine whether or not to re-verify a domain.
+     */
+    static boolean isStateVerified(@DomainVerificationState.State int state) {
+        switch (state) {
+            case DomainVerificationState.STATE_SUCCESS:
+            case DomainVerificationState.STATE_APPROVED:
+            case DomainVerificationState.STATE_MIGRATED:
+            case DomainVerificationState.STATE_RESTORED:
+            case DomainVerificationState.STATE_SYS_CONFIG:
+                return true;
+            case DomainVerificationState.STATE_NO_RESPONSE:
+            case DomainVerificationState.STATE_DENIED:
+            case DomainVerificationState.STATE_LEGACY_FAILURE:
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Checks if a state is modifiable by the domain verification agent. This is useful as the
+     * platform may add new state codes in newer versions, and older verification agents can use
+     * this method to determine if a state can be changed without having to be aware of what the
+     * new state means.
+     */
+    static boolean isStateModifiable(@DomainVerificationState.State int state) {
+        switch (state) {
+            case DomainVerificationState.STATE_NO_RESPONSE:
+            case DomainVerificationState.STATE_SUCCESS:
+            case DomainVerificationState.STATE_MIGRATED:
+            case DomainVerificationState.STATE_RESTORED:
+            case DomainVerificationState.STATE_LEGACY_FAILURE:
+                return true;
+            case DomainVerificationState.STATE_APPROVED:
+            case DomainVerificationState.STATE_DENIED:
+            case DomainVerificationState.STATE_SYS_CONFIG:
+                return false;
+            default:
+                return state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+        }
+    }
+
+    /**
+     * For determine re-verify policy. This is hidden from the domain verification agent so that
+     * no behavior is made based on the result.
+     * @hide
+     */
+    static boolean isStateDefault(@DomainVerificationState.State int state) {
+        switch (state) {
+            case DomainVerificationState.STATE_NO_RESPONSE:
+            case DomainVerificationState.STATE_MIGRATED:
+            case DomainVerificationState.STATE_RESTORED:
+                return true;
+            case DomainVerificationState.STATE_SUCCESS:
+            case DomainVerificationState.STATE_APPROVED:
+            case DomainVerificationState.STATE_DENIED:
+            case DomainVerificationState.STATE_LEGACY_FAILURE:
+            case DomainVerificationState.STATE_SYS_CONFIG:
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Used to iterate all {@link DomainVerificationInfo} values to do cleanup or retries. This is
+     * usually a heavy workload and should be done infrequently.
+     *
+     * @return the current snapshot of package names with valid autoVerify URLs.
+     */
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
+    List<String> getValidVerificationPackageNames();
+
+    /**
+     * Retrieves the domain verification state for a given package.
+     *
+     * @return the data for the package, or null if it does not declare any autoVerify domains
+     * @throws NameNotFoundException If the package is unavailable. This is an unrecoverable error
+     *                               and should not be re-tried except on a time scheduled basis.
+     */
+    @Nullable
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
+            android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+    })
+    DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
+            throws NameNotFoundException;
+
+    /**
+     * Change the verification status of the {@param domains} of the package associated with
+     * {@param domainSetId}.
+     *
+     * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
+     * @param domains     List of host names to change the state of.
+     * @param state       See {@link DomainVerificationInfo#getHostToStateMap()}.
+     * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
+     *                                  invalid. This usually means the work being processed by the
+     *                                  verification agent is outdated and a new request should
+     *                                  be scheduled, if one has not already been done as part of
+     *                                  the {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION}
+     *                                  broadcast.
+     * @throws NameNotFoundException    If the ID is known to be good, but the package is
+     *                                  unavailable. This may be because the package is
+     *                                  installed on a volume that is no longer mounted. This
+     *                                  error is unrecoverable until the package is available
+     *                                  again, and should not be re-tried except on a time
+     *                                  scheduled basis.
+     */
+    @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
+    void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+            @DomainVerificationState.State int state) throws NameNotFoundException;
+
+    /**
+     * TODO(b/178525735): This documentation is incorrect in the context of UX changes.
+     * Change whether the given {@param packageName} is allowed to automatically open verified
+     * HTTP/HTTPS domains. The final state is determined along with the verification status for the
+     * specific domain being opened and other system state. An app with this enabled is not
+     * guaranteed to be the sole link handler for its domains.
+     *
+     * By default, all apps are allowed to open verified links. Users must disable them explicitly.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
+    void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed)
+            throws NameNotFoundException;
+
+    /**
+     * Update the recorded user selection for the given {@param domains} for the given {@param
+     * domainSetId}. This state is recorded for the lifetime of a domain for a package on device,
+     * and will never be reset by the system short of an app data clear.
+     *
+     * This state is stored per device user. If another user needs to be changed, the appropriate
+     * permissions must be acquired and
+     * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used.
+     *
+     * This will be combined with the verification status and other system state to determine which
+     * application is launched to handle an app link.
+     *
+     * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
+     * @param domains     The domains to toggle the state of.
+     * @param enabled     Whether or not the app should automatically open the domains specified.
+     * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
+     *                                  invalid.
+     * @throws NameNotFoundException    If the ID is known to be good, but the package is
+     *                                  unavailable. This may be because the package is
+     *                                  installed on a volume that is no longer mounted. This
+     *                                  error is unrecoverable until the package is available
+     *                                  again, and should not be re-tried except on a time
+     *                                  scheduled basis.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
+    void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+            @NonNull Set<String> domains, boolean enabled) throws NameNotFoundException;
+
+    /**
+     * Retrieve the user selection data for the given {@param packageName} and the current user.
+     * It is the responsibility of the caller to ensure that the
+     * {@link DomainVerificationUserSelection#getIdentifier()} matches any prior API calls.
+     *
+     * This state is stored per device user. If another user needs to be accessed, the appropriate
+     * permissions must be acquired and
+     * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used.
+     *
+     * @param packageName The app to query state for.
+     * @return the user selection verification data for the given package for the current user,
+     * or null if the package does not declare any HTTP/HTTPS domains.
+     */
+    @Nullable
+    @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
+    DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String packageName)
+            throws NameNotFoundException;
+
+    /**
+     * Thrown if a {@link DomainVerificationInfo#getIdentifier()}} or an associated set of domains
+     * provided by the caller is no longer valid. This may be recoverable, and the caller should
+     * re-query the package name associated with the ID using
+     * {@link #getDomainVerificationInfo(String)} in order to check. If that also fails, then the
+     * package is no longer known to the device and thus all pending work for it should be dropped.
+     *
+     * @hide
+     */
+    class InvalidDomainSetException extends IllegalArgumentException {
+
+        public static final int REASON_ID_NULL = 1;
+        public static final int REASON_ID_INVALID = 2;
+        public static final int REASON_SET_NULL_OR_EMPTY = 3;
+        public static final int REASON_UNKNOWN_DOMAIN = 4;
+
+        /** @hide */
+        @IntDef({
+                REASON_ID_NULL,
+                REASON_ID_INVALID,
+                REASON_SET_NULL_OR_EMPTY,
+                REASON_UNKNOWN_DOMAIN
+        })
+        public @interface Reason {
+        }
+
+        public static String buildMessage(@Nullable UUID domainSetId, @Nullable String packageName,
+                @Reason int reason) {
+            switch (reason) {
+                case REASON_ID_NULL:
+                    return "Domain set ID cannot be null";
+                case REASON_ID_INVALID:
+                    return "Domain set ID " + domainSetId + " has been invalidated";
+                case REASON_SET_NULL_OR_EMPTY:
+                    return "Domain set cannot be null or empty";
+                case REASON_UNKNOWN_DOMAIN:
+                    return "Domain set contains value that was not declared by the target package "
+                            + packageName;
+                default:
+                    return "Unknown failure";
+            }
+        }
+
+        @Reason
+        private final int mReason;
+
+        @Nullable
+        private final UUID mDomainSetId;
+
+        @Nullable
+        private final String mPackageName;
+
+        /** @hide */
+        public InvalidDomainSetException(@Nullable UUID domainSetId, @Nullable String packageName,
+                @Reason int reason) {
+            super(buildMessage(domainSetId, packageName, reason));
+            mDomainSetId = domainSetId;
+            mPackageName = packageName;
+            mReason = reason;
+        }
+
+        @Nullable
+        public UUID getDomainSetId() {
+            return mDomainSetId;
+        }
+
+        @Nullable
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        @Reason
+        public int getReason() {
+            return mReason;
+        }
+    }
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java
new file mode 100644
index 0000000..5938def
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.verify.domain.IDomainVerificationManager;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @hide
+ */
+@SuppressWarnings("RedundantThrows")
+public class DomainVerificationManagerImpl implements DomainVerificationManager {
+
+    public static final int ERROR_INVALID_DOMAIN_SET = 1;
+    public static final int ERROR_NAME_NOT_FOUND = 2;
+
+    @IntDef(prefix = { "ERROR_" }, value = {
+            ERROR_INVALID_DOMAIN_SET,
+            ERROR_NAME_NOT_FOUND,
+    })
+    private @interface Error {
+    }
+
+    private final Context mContext;
+
+    private final IDomainVerificationManager mDomainVerificationManager;
+
+    public DomainVerificationManagerImpl(Context context,
+            IDomainVerificationManager domainVerificationManager) {
+        mContext = context;
+        mDomainVerificationManager = domainVerificationManager;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getValidVerificationPackageNames() {
+        try {
+            return mDomainVerificationManager.getValidVerificationPackageNames();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Nullable
+    @Override
+    public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
+            throws NameNotFoundException {
+        try {
+            return mDomainVerificationManager.getDomainVerificationInfo(packageName);
+        } catch (Exception e) {
+            Exception converted = rethrow(e, packageName);
+            if (converted instanceof NameNotFoundException) {
+                throw (NameNotFoundException) converted;
+            } else if (converted instanceof RuntimeException) {
+                throw (RuntimeException) converted;
+            } else {
+                throw new RuntimeException(converted);
+            }
+        }
+    }
+
+    @Override
+    public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+            int state) throws IllegalArgumentException, NameNotFoundException {
+        try {
+            mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
+                    new ArrayList<>(domains), state);
+        } catch (Exception e) {
+            Exception converted = rethrow(e, domainSetId);
+            if (converted instanceof NameNotFoundException) {
+                throw (NameNotFoundException) converted;
+            } else if (converted instanceof RuntimeException) {
+                throw (RuntimeException) converted;
+            } else {
+                throw new RuntimeException(converted);
+            }
+        }
+    }
+
+    @Override
+    public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
+            boolean allowed) throws NameNotFoundException {
+        try {
+            mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(packageName,
+                    allowed, mContext.getUserId());
+        } catch (Exception e) {
+            Exception converted = rethrow(e, packageName);
+            if (converted instanceof NameNotFoundException) {
+                throw (NameNotFoundException) converted;
+            } else if (converted instanceof RuntimeException) {
+                throw (RuntimeException) converted;
+            } else {
+                throw new RuntimeException(converted);
+            }
+        }
+    }
+
+    @Override
+    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+            @NonNull Set<String> domains, boolean enabled)
+            throws IllegalArgumentException, NameNotFoundException {
+        try {
+            mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
+                    new ArrayList<>(domains), enabled, mContext.getUserId());
+        } catch (Exception e) {
+            Exception converted = rethrow(e, domainSetId);
+            if (converted instanceof NameNotFoundException) {
+                throw (NameNotFoundException) converted;
+            } else if (converted instanceof RuntimeException) {
+                throw (RuntimeException) converted;
+            } else {
+                throw new RuntimeException(converted);
+            }
+        }
+    }
+
+    @Nullable
+    @Override
+    public DomainVerificationUserSelection getDomainVerificationUserSelection(
+            @NonNull String packageName) throws NameNotFoundException {
+        try {
+            return mDomainVerificationManager.getDomainVerificationUserSelection(packageName,
+                    mContext.getUserId());
+        } catch (Exception e) {
+            Exception converted = rethrow(e, packageName);
+            if (converted instanceof NameNotFoundException) {
+                throw (NameNotFoundException) converted;
+            } else if (converted instanceof RuntimeException) {
+                throw (RuntimeException) converted;
+            } else {
+                throw new RuntimeException(converted);
+            }
+        }
+    }
+
+    private Exception rethrow(Exception exception, @Nullable UUID domainSetId) {
+        return rethrow(exception, domainSetId, null);
+    }
+
+    private Exception rethrow(Exception exception, @Nullable String packageName) {
+        return rethrow(exception, null, packageName);
+    }
+
+    private Exception rethrow(Exception exception, @Nullable UUID domainSetId,
+            @Nullable String packageName) {
+        if (exception instanceof ServiceSpecificException) {
+            int packedErrorCode = ((ServiceSpecificException) exception).errorCode;
+            if (packageName == null) {
+                packageName = exception.getMessage();
+            }
+
+            @Error int managerErrorCode = packedErrorCode & 0xFFFF;
+            switch (managerErrorCode) {
+                case ERROR_INVALID_DOMAIN_SET:
+                    int errorSpecificCode = packedErrorCode >> 16;
+                    return new IllegalArgumentException(InvalidDomainSetException.buildMessage(
+                            domainSetId, packageName, errorSpecificCode));
+                case ERROR_NAME_NOT_FOUND:
+                    return new NameNotFoundException(packageName);
+                default:
+                    return exception;
+            }
+        } else if (exception instanceof RemoteException) {
+            return ((RemoteException) exception).rethrowFromSystemServer();
+        } else {
+            return exception;
+        }
+    }
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java b/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java
new file mode 100644
index 0000000..473abce
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Intent;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Set;
+
+/**
+ * Request object sent in the {@link Intent} that's broadcast to the domain verification
+ * agent, retrieved through {@link DomainVerificationManager#EXTRA_VERIFICATION_REQUEST}.
+ * <p>
+ * This contains the set of packages which have been invalidated and will require
+ * re-verification. The exact domains can be retrieved with
+ * {@link DomainVerificationManager#getDomainVerificationInfo(String)}
+ *
+ * @hide
+ */
+@SuppressWarnings("DefaultAnnotationParam")
+@DataClass(genHiddenConstructor = true, genAidl = false, genEqualsHashCode = true)
+@SystemApi
+public final class DomainVerificationRequest implements Parcelable {
+
+    /**
+     * The package names of the apps that need to be verified. The receiver should call
+     * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
+     * these values to get the actual set of domains that need to be acted on.
+     */
+    @NonNull
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForStringSet.class)
+    private final Set<String> mPackageNames;
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new DomainVerificationRequest.
+     *
+     * @param packageNames
+     *   The package names of the apps that need to be verified. The receiver should call
+     *   {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
+     *   these values to get the actual set of domains that need to be acted on.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public DomainVerificationRequest(
+            @NonNull Set<String> packageNames) {
+        this.mPackageNames = packageNames;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageNames);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The package names of the apps that need to be verified. The receiver should call
+     * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
+     * these values to get the actual set of domains that need to be acted on.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Set<String> getPackageNames() {
+        return mPackageNames;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(DomainVerificationRequest other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        DomainVerificationRequest that = (DomainVerificationRequest) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mPackageNames, that.mPackageNames);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mPackageNames);
+        return _hash;
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<Set<String>> sParcellingForPackageNames =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForStringSet.class);
+    static {
+        if (sParcellingForPackageNames == null) {
+            sParcellingForPackageNames = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForStringSet());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        sParcellingForPackageNames.parcel(mPackageNames, dest, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ DomainVerificationRequest(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        Set<String> packageNames = sParcellingForPackageNames.unparcel(in);
+
+        this.mPackageNames = packageNames;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageNames);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<DomainVerificationRequest> CREATOR
+            = new Parcelable.Creator<DomainVerificationRequest>() {
+        @Override
+        public DomainVerificationRequest[] newArray(int size) {
+            return new DomainVerificationRequest[size];
+        }
+
+        @Override
+        public DomainVerificationRequest createFromParcel(@NonNull android.os.Parcel in) {
+            return new DomainVerificationRequest(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1611862814990L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java",
+            inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForStringSet.class) java.util.Set<java.lang.String> mPackageNames\nclass DomainVerificationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationState.java b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
new file mode 100644
index 0000000..17593ef
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.IntDef;
+
+/**
+ * @hide
+ */
+public interface DomainVerificationState {
+
+    /**
+     * @hide
+     */
+    @IntDef({
+            STATE_NO_RESPONSE,
+            STATE_SUCCESS,
+            STATE_MIGRATED,
+            STATE_RESTORED,
+            STATE_APPROVED,
+            STATE_DENIED,
+            STATE_LEGACY_FAILURE,
+            STATE_SYS_CONFIG,
+            STATE_FIRST_VERIFIER_DEFINED
+    })
+    @interface State {
+    }
+
+    // TODO(b/159952358): Document all the places that states need to be updated when one is added
+    /**
+     * @see DomainVerificationManager#STATE_NO_RESPONSE
+     */
+    int STATE_NO_RESPONSE = 0;
+
+    /**
+     * @see DomainVerificationManager#STATE_SUCCESS
+     */
+    int STATE_SUCCESS = 1;
+
+    /**
+     * The system has chosen to ignore the verification agent's opinion on whether the domain should
+     * be verified. This will treat the domain as verified.
+     */
+    int STATE_APPROVED = 2;
+
+    /**
+     * The system has chosen to ignore the verification agent's opinion on whether the domain should
+     * be verified. This will treat the domain as unverified.
+     */
+    int STATE_DENIED = 3;
+
+    /**
+     * The state was migrated from the previous intent filter verification API. This will treat the
+     * domain as verified, but it should be updated by the verification agent. The older API's
+     * collection and handling of verifying domains may lead to improperly migrated state.
+     */
+    int STATE_MIGRATED = 4;
+
+    /**
+     * The state was restored from a user backup or by the system. This is treated as if the domain
+     * was verified, but the verification agent may choose to re-verify this domain to be certain
+     * nothing has changed since the snapshot.
+     */
+    int STATE_RESTORED = 5;
+
+    /**
+     * The domain was failed by a legacy intent filter verification agent from v1 of the API. This
+     * is made distinct from {@link #STATE_FIRST_VERIFIER_DEFINED} to prevent any v2 verification
+     * agent from misinterpreting the result, since {@link #STATE_FIRST_VERIFIER_DEFINED} is agent
+     * specific and can be defined as a special error code.
+     */
+    int STATE_LEGACY_FAILURE = 6;
+
+    /**
+     * The application has been granted auto verification for all domains by configuration on the
+     * system image.
+     *
+     * TODO: Can be stored per-package rather than for all domains for a package to save memory.
+     */
+    int STATE_SYS_CONFIG = 7;
+
+    /**
+     * @see DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED
+     */
+    int STATE_FIRST_VERIFIER_DEFINED = 0b10000000000;
+}
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
similarity index 79%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
index 19b20f2..ddb5ef8 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-package android.graphics.fonts;
+package android.content.pm.verify.domain;
 
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+parcelable DomainVerificationUserSelection;
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
new file mode 100644
index 0000000..8d16f75
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Contains the user selection state for a package. This means all web HTTP(S) domains
+ * declared by a package in its manifest, whether or not they were marked for auto
+ * verification.
+ * <p>
+ * By default, all apps are allowed to automatically open links with domains that they've
+ * successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}.
+ * The user can decide to disable this, disallowing the application from opening these
+ * links.
+ * <p>
+ * Separately, independent of this toggle, the user can choose specific domains to allow
+ * an app to open, which is reflected as part of {@link #getHostToUserSelectionMap()},
+ * which maps the domain name to the true/false state of whether it was enabled by the user.
+ * <p>
+ * These values can be changed through the
+ * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
+ * boolean)} and
+ * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
+ * boolean)} APIs.
+ * <p>
+ * Note that because state is per user, if a different user needs to be changed, one will
+ * need to use {@link Context#createContextAsUser(UserHandle, int)} and hold the
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+ *
+ * @hide
+ */
+@SystemApi
+@SuppressWarnings("DefaultAnnotationParam")
+@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
+        genEqualsHashCode = true)
+public final class DomainVerificationUserSelection implements Parcelable {
+
+    /**
+     * @see DomainVerificationInfo#getIdentifier
+     */
+    @NonNull
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForUUID.class)
+    private final UUID mIdentifier;
+
+    /**
+     * The package name that this data corresponds to.
+     */
+    @NonNull
+    private final String mPackageName;
+
+    /**
+     * The user that this data corresponds to.
+     */
+    @NonNull
+    private final UserHandle mUser;
+
+    /**
+     * Whether or not this package is allowed to open links.
+     */
+    @NonNull
+    private final boolean mLinkHandlingAllowed;
+
+    /**
+     * Retrieve the existing user selection state for the matching
+     * {@link #getPackageName()}, as was previously set by
+     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
+     * boolean)}.
+     *
+     * @return Map of hosts to enabled state for the given package and user.
+     */
+    @NonNull
+    private final Map<String, Boolean> mHostToUserSelectionMap;
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new DomainVerificationUserSelection.
+     *
+     * @param packageName
+     *   The package name that this data corresponds to.
+     * @param user
+     *   The user that this data corresponds to.
+     * @param linkHandlingAllowed
+     *   Whether or not this package is allowed to open links.
+     * @param hostToUserSelectionMap
+     *   Retrieve the existing user selection state for the matching
+     *   {@link #getPackageName()}, as was previously set by
+     *   {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
+     *   boolean)}.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public DomainVerificationUserSelection(
+            @NonNull UUID identifier,
+            @NonNull String packageName,
+            @NonNull UserHandle user,
+            @NonNull boolean linkHandlingAllowed,
+            @NonNull Map<String,Boolean> hostToUserSelectionMap) {
+        this.mIdentifier = identifier;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mIdentifier);
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mUser = user;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mUser);
+        this.mLinkHandlingAllowed = linkHandlingAllowed;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mLinkHandlingAllowed);
+        this.mHostToUserSelectionMap = hostToUserSelectionMap;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHostToUserSelectionMap);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * @see DomainVerificationInfo#getIdentifier
+     */
+    @DataClass.Generated.Member
+    public @NonNull UUID getIdentifier() {
+        return mIdentifier;
+    }
+
+    /**
+     * The package name that this data corresponds to.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * The user that this data corresponds to.
+     */
+    @DataClass.Generated.Member
+    public @NonNull UserHandle getUser() {
+        return mUser;
+    }
+
+    /**
+     * Whether or not this package is allowed to open links.
+     */
+    @DataClass.Generated.Member
+    public @NonNull boolean isLinkHandlingAllowed() {
+        return mLinkHandlingAllowed;
+    }
+
+    /**
+     * Retrieve the existing user selection state for the matching
+     * {@link #getPackageName()}, as was previously set by
+     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
+     * boolean)}.
+     *
+     * @return Map of hosts to enabled state for the given package and user.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Map<String,Boolean> getHostToUserSelectionMap() {
+        return mHostToUserSelectionMap;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "DomainVerificationUserSelection { " +
+                "identifier = " + mIdentifier + ", " +
+                "packageName = " + mPackageName + ", " +
+                "user = " + mUser + ", " +
+                "linkHandlingAllowed = " + mLinkHandlingAllowed + ", " +
+                "hostToUserSelectionMap = " + mHostToUserSelectionMap +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(DomainVerificationUserSelection other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        DomainVerificationUserSelection that = (DomainVerificationUserSelection) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mIdentifier, that.mIdentifier)
+                && java.util.Objects.equals(mPackageName, that.mPackageName)
+                && java.util.Objects.equals(mUser, that.mUser)
+                && mLinkHandlingAllowed == that.mLinkHandlingAllowed
+                && java.util.Objects.equals(mHostToUserSelectionMap, that.mHostToUserSelectionMap);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mIdentifier);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mUser);
+        _hash = 31 * _hash + Boolean.hashCode(mLinkHandlingAllowed);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mHostToUserSelectionMap);
+        return _hash;
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<UUID> sParcellingForIdentifier =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForUUID.class);
+    static {
+        if (sParcellingForIdentifier == null) {
+            sParcellingForIdentifier = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForUUID());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mLinkHandlingAllowed) flg |= 0x8;
+        dest.writeByte(flg);
+        sParcellingForIdentifier.parcel(mIdentifier, dest, flags);
+        dest.writeString(mPackageName);
+        dest.writeTypedObject(mUser, flags);
+        dest.writeMap(mHostToUserSelectionMap);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ DomainVerificationUserSelection(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        boolean linkHandlingAllowed = (flg & 0x8) != 0;
+        UUID identifier = sParcellingForIdentifier.unparcel(in);
+        String packageName = in.readString();
+        UserHandle user = (UserHandle) in.readTypedObject(UserHandle.CREATOR);
+        Map<String,Boolean> hostToUserSelectionMap = new java.util.LinkedHashMap<>();
+        in.readMap(hostToUserSelectionMap, Boolean.class.getClassLoader());
+
+        this.mIdentifier = identifier;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mIdentifier);
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mUser = user;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mUser);
+        this.mLinkHandlingAllowed = linkHandlingAllowed;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mLinkHandlingAllowed);
+        this.mHostToUserSelectionMap = hostToUserSelectionMap;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHostToUserSelectionMap);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<DomainVerificationUserSelection> CREATOR
+            = new Parcelable.Creator<DomainVerificationUserSelection>() {
+        @Override
+        public DomainVerificationUserSelection[] newArray(int size) {
+            return new DomainVerificationUserSelection[size];
+        }
+
+        @Override
+        public DomainVerificationUserSelection createFromParcel(@NonNull android.os.Parcel in) {
+            return new DomainVerificationUserSelection(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1611799495498L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java",
+            inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Boolean> mHostToUserSelectionMap\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
new file mode 100644
index 0000000..21dd623b
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import java.util.List;
+
+/**
+ * @see DomainVerificationManager
+ * @hide
+ */
+interface IDomainVerificationManager {
+
+    List<String> getValidVerificationPackageNames();
+
+    @nullable
+    DomainVerificationInfo getDomainVerificationInfo(String packageName);
+
+    @nullable
+    DomainVerificationUserSelection getDomainVerificationUserSelection(String packageName,
+            int userId);
+
+    void setDomainVerificationStatus(String domainSetId, in List<String> domains, int state);
+
+    void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed, int userId);
+
+    void setDomainVerificationUserSelection(String domainSetId, in List<String> domains,
+            boolean enabled, int userId);
+}
diff --git a/core/java/android/content/pm/verify/domain/TEST_MAPPING b/core/java/android/content/pm/verify/domain/TEST_MAPPING
new file mode 100644
index 0000000..c6c9791
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "PackageManagerServiceUnitTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.pm.test.verify.domain"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java
index f0b218c..abb4f9f 100644
--- a/core/java/android/graphics/fonts/FontManager.java
+++ b/core/java/android/graphics/fonts/FontManager.java
@@ -261,7 +261,7 @@
             @IntRange(from = 0) int baseVersion
     ) {
         try {
-            return mIFontManager.updateFont(pfd, signature, baseVersion);
+            return mIFontManager.updateFont(baseVersion, new FontUpdateRequest(pfd, signature));
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to call updateFont API", e);
             return RESULT_ERROR_REMOTE_EXCEPTION;
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/core/java/android/graphics/fonts/FontUpdateRequest.aidl
similarity index 95%
rename from core/java/android/graphics/fonts/SystemFontState.aidl
rename to core/java/android/graphics/fonts/FontUpdateRequest.aidl
index 19b20f2..f6cb373 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/core/java/android/graphics/fonts/FontUpdateRequest.aidl
@@ -17,4 +17,4 @@
 package android.graphics.fonts;
 
 /** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+parcelable FontUpdateRequest;
\ No newline at end of file
diff --git a/core/java/android/graphics/fonts/FontUpdateRequest.java b/core/java/android/graphics/fonts/FontUpdateRequest.java
new file mode 100644
index 0000000..db047f8
--- /dev/null
+++ b/core/java/android/graphics/fonts/FontUpdateRequest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 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.graphics.fonts;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+/**
+ * Represents a font update request. Currently only font install request is supported.
+ * @hide
+ */
+// TODO: Support font config update.
+public final class FontUpdateRequest implements Parcelable {
+
+    public static final Creator<FontUpdateRequest> CREATOR = new Creator<FontUpdateRequest>() {
+        @Override
+        public FontUpdateRequest createFromParcel(Parcel in) {
+            return new FontUpdateRequest(in);
+        }
+
+        @Override
+        public FontUpdateRequest[] newArray(int size) {
+            return new FontUpdateRequest[size];
+        }
+    };
+
+    @NonNull
+    private final ParcelFileDescriptor mFd;
+    @NonNull
+    private final byte[] mSignature;
+
+    public FontUpdateRequest(@NonNull ParcelFileDescriptor fd, @NonNull byte[] signature) {
+        mFd = fd;
+        mSignature = signature;
+    }
+
+    private FontUpdateRequest(Parcel in) {
+        mFd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
+        mSignature = in.readBlob();
+    }
+
+    @NonNull
+    public ParcelFileDescriptor getFd() {
+        return mFd;
+    }
+
+    @NonNull
+    public byte[] getSignature() {
+        return mSignature;
+    }
+
+    @Override
+    public int describeContents() {
+        return Parcelable.CONTENTS_FILE_DESCRIPTOR;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mFd, flags);
+        dest.writeBlob(mSignature);
+    }
+}
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index e2d836c..a6c6b46 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -65,6 +65,23 @@
     /** If night mode color filter is active this will be the temperature in kelvin */
     public final int colorTemperature;
 
+    /** Whether the bright color reduction color transform is active */
+    public final boolean reduceBrightColors;
+
+    /** How strong the bright color reduction color transform is set (only applicable if active),
+     *  specified as an integer from 0 - 100, inclusive. This value (scaled to 0-1, inclusive) is
+     *  then used in Ynew = (a * scaledStrength^2 + b * scaledStrength + c) * Ycurrent, where a, b,
+     *  and c are coefficients provided in the bright color reduction coefficient matrix, and
+     *  Ycurrent is the current hardware brightness in nits.
+     */
+    public final int reduceBrightColorsStrength;
+
+    /** Applied offset for the bright color reduction color transform (only applicable if active).
+     *  The offset is computed by summing the coefficients a, b, and c, from the coefficient matrix
+     *  and multiplying by the current brightness.
+     */
+    public final float reduceBrightColorsOffset;
+
     /** Brightness level before slider adjustment */
     public final float lastBrightness;
 
@@ -105,8 +122,9 @@
     private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
             int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
             float powerBrightnessFactor, boolean nightMode, int colorTemperature,
-            float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness,
-            long[] colorValueBuckets, long colorSampleDuration) {
+            boolean reduceBrightColors, int reduceBrightColorsStrength,
+            float reduceBrightColorsOffset, float lastBrightness, boolean isDefaultBrightnessConfig,
+            boolean isUserSetBrightness, long[] colorValueBuckets, long colorSampleDuration) {
         this.brightness = brightness;
         this.timeStamp = timeStamp;
         this.packageName = packageName;
@@ -117,6 +135,9 @@
         this.powerBrightnessFactor = powerBrightnessFactor;
         this.nightMode = nightMode;
         this.colorTemperature = colorTemperature;
+        this.reduceBrightColors = reduceBrightColors;
+        this.reduceBrightColorsStrength = reduceBrightColorsStrength;
+        this.reduceBrightColorsOffset = reduceBrightColorsOffset;
         this.lastBrightness = lastBrightness;
         this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
         this.isUserSetBrightness = isUserSetBrightness;
@@ -136,6 +157,9 @@
         this.powerBrightnessFactor = other.powerBrightnessFactor;
         this.nightMode = other.nightMode;
         this.colorTemperature = other.colorTemperature;
+        this.reduceBrightColors = other.reduceBrightColors;
+        this.reduceBrightColorsStrength = other.reduceBrightColorsStrength;
+        this.reduceBrightColorsOffset = other.reduceBrightColorsOffset;
         this.lastBrightness = other.lastBrightness;
         this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig;
         this.isUserSetBrightness = other.isUserSetBrightness;
@@ -154,6 +178,9 @@
         powerBrightnessFactor = source.readFloat();
         nightMode = source.readBoolean();
         colorTemperature = source.readInt();
+        reduceBrightColors = source.readBoolean();
+        reduceBrightColorsStrength = source.readInt();
+        reduceBrightColorsOffset = source.readFloat();
         lastBrightness = source.readFloat();
         isDefaultBrightnessConfig = source.readBoolean();
         isUserSetBrightness = source.readBoolean();
@@ -188,6 +215,9 @@
         dest.writeFloat(powerBrightnessFactor);
         dest.writeBoolean(nightMode);
         dest.writeInt(colorTemperature);
+        dest.writeBoolean(reduceBrightColors);
+        dest.writeInt(reduceBrightColorsStrength);
+        dest.writeFloat(reduceBrightColorsOffset);
         dest.writeFloat(lastBrightness);
         dest.writeBoolean(isDefaultBrightnessConfig);
         dest.writeBoolean(isUserSetBrightness);
@@ -207,6 +237,9 @@
         private float mPowerBrightnessFactor;
         private boolean mNightMode;
         private int mColorTemperature;
+        private boolean mReduceBrightColors;
+        private int mReduceBrightColorsStrength;
+        private float mReduceBrightColorsOffset;
         private float mLastBrightness;
         private boolean mIsDefaultBrightnessConfig;
         private boolean mIsUserSetBrightness;
@@ -273,6 +306,24 @@
             return this;
         }
 
+        /** {@see BrightnessChangeEvent#reduceBrightColors} */
+        public Builder setReduceBrightColors(boolean reduceBrightColors) {
+            mReduceBrightColors = reduceBrightColors;
+            return this;
+        }
+
+        /** {@see BrightnessChangeEvent#reduceBrightColorsStrength} */
+        public Builder setReduceBrightColorsStrength(int strength) {
+            mReduceBrightColorsStrength = strength;
+            return this;
+        }
+
+        /** {@see BrightnessChangeEvent#reduceBrightColorsOffset} */
+        public Builder setReduceBrightColorsOffset(float offset) {
+            mReduceBrightColorsOffset = offset;
+            return this;
+        }
+
         /** {@see BrightnessChangeEvent#lastBrightness} */
         public Builder setLastBrightness(float lastBrightness) {
             mLastBrightness = lastBrightness;
@@ -304,7 +355,8 @@
         public BrightnessChangeEvent build() {
             return new BrightnessChangeEvent(mBrightness, mTimeStamp,
                     mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
-                    mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness,
+                    mPowerBrightnessFactor, mNightMode, mColorTemperature, mReduceBrightColors,
+                    mReduceBrightColorsStrength, mReduceBrightColorsOffset, mLastBrightness,
                     mIsDefaultBrightnessConfig, mIsUserSetBrightness, mColorValueBuckets,
                     mColorSampleDuration);
         }
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index efeb89e..e247df3 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -438,6 +438,56 @@
     }
 
     /**
+     * Enables or disables reduce bright colors.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+    public boolean setReduceBrightColorsActivated(boolean activated) {
+        return mManager.setReduceBrightColorsActivated(activated);
+    }
+
+    /**
+     * Returns whether reduce bright colors is currently enabled.
+     *
+     * @hide
+     */
+    public boolean isReduceBrightColorsActivated() {
+        return mManager.isReduceBrightColorsActivated();
+    }
+
+    /**
+     * Set the strength level of bright color reduction to apply to the display.
+     *
+     * @param strength 0-100 (inclusive), where 100 is full strength
+     * @return whether the change was applied successfully
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+    public boolean setReduceBrightColorsStrength(@IntRange(from = 0, to = 100) int strength) {
+        return mManager.setReduceBrightColorsStrength(strength);
+    }
+
+    /**
+     * Gets the strength of the bright color reduction transform.
+     *
+     * @hide
+     */
+    public int getReduceBrightColorsStrength() {
+        return mManager.getReduceBrightColorsStrength();
+    }
+
+    /**
+     * Gets the brightness impact of the bright color reduction transform, as in the factor by which
+     * the current brightness (in nits) should be multiplied to obtain the brightness offset 'b'.
+     *
+     * @hide
+     */
+    public float getReduceBrightColorsOffsetFactor() {
+        return mManager.getReduceBrightColorsOffsetFactor();
+    }
+
+    /**
      * Returns {@code true} if Night Display is supported by the device.
      *
      * @hide
@@ -478,6 +528,15 @@
     }
 
     /**
+     * Returns {@code true} if reduce bright colors is supported by the device.
+     *
+     * @hide
+     */
+    public static boolean isReduceBrightColorsAvailable(Context context) {
+        return context.getResources().getBoolean(R.bool.config_reduceBrightColorsAvailable);
+    }
+
+    /**
      * Check if the color transforms are color accelerated. Some transforms are experimental only
      * on non-accelerated platforms due to the performance implications.
      *
@@ -678,6 +737,46 @@
             }
         }
 
+        boolean isReduceBrightColorsActivated() {
+            try {
+                return mCdm.isReduceBrightColorsActivated();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        boolean setReduceBrightColorsActivated(boolean activated) {
+            try {
+                return mCdm.setReduceBrightColorsActivated(activated);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        int getReduceBrightColorsStrength() {
+            try {
+                return mCdm.getReduceBrightColorsStrength();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        boolean setReduceBrightColorsStrength(int strength) {
+            try {
+                return mCdm.setReduceBrightColorsStrength(strength);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        float getReduceBrightColorsOffsetFactor() {
+            try {
+                return mCdm.getReduceBrightColorsOffsetFactor();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
         int getColorMode() {
             try {
                 return mCdm.getColorMode();
diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl
index 7b1033d..ef4f5f9 100644
--- a/core/java/android/hardware/display/IColorDisplayManager.aidl
+++ b/core/java/android/hardware/display/IColorDisplayManager.aidl
@@ -45,4 +45,10 @@
 
     boolean isDisplayWhiteBalanceEnabled();
     boolean setDisplayWhiteBalanceEnabled(boolean enabled);
+
+    boolean isReduceBrightColorsActivated();
+    boolean setReduceBrightColorsActivated(boolean activated);
+    int getReduceBrightColorsStrength();
+    boolean setReduceBrightColorsStrength(int strength);
+    float getReduceBrightColorsOffsetFactor();
 }
\ No newline at end of file
diff --git a/core/java/android/hardware/face/FaceAuthenticationFrame.java b/core/java/android/hardware/face/FaceAuthenticationFrame.java
new file mode 100644
index 0000000..f39d634
--- /dev/null
+++ b/core/java/android/hardware/face/FaceAuthenticationFrame.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data model for a frame captured during face authentication.
+ *
+ * @hide
+ */
+public final class FaceAuthenticationFrame implements Parcelable {
+    @NonNull private final FaceDataFrame mData;
+
+    /**
+     * Data model for a frame captured during face authentication.
+     *
+     * @param data Information about the current frame.
+     */
+    public FaceAuthenticationFrame(@NonNull FaceDataFrame data) {
+        mData = data;
+    }
+
+    /**
+     * @return Information about the current frame.
+     */
+    @NonNull
+    public FaceDataFrame getData() {
+        return mData;
+    }
+
+    private FaceAuthenticationFrame(@NonNull Parcel source) {
+        mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mData, flags);
+    }
+
+    public static final Creator<FaceAuthenticationFrame> CREATOR =
+            new Creator<FaceAuthenticationFrame>() {
+
+        @Override
+        public FaceAuthenticationFrame createFromParcel(Parcel source) {
+            return new FaceAuthenticationFrame(source);
+        }
+
+        @Override
+        public FaceAuthenticationFrame[] newArray(int size) {
+            return new FaceAuthenticationFrame[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/face/FaceDataFrame.java b/core/java/android/hardware/face/FaceDataFrame.java
new file mode 100644
index 0000000..3a0e09b
--- /dev/null
+++ b/core/java/android/hardware/face/FaceDataFrame.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+ *
+ * @hide
+ */
+public final class FaceDataFrame implements Parcelable {
+    private final int mAcquiredInfo;
+    private final int mVendorCode;
+    private final float mPan;
+    private final float mTilt;
+    private final float mDistance;
+    private final boolean mIsCancellable;
+
+    /**
+     * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+     *
+     * @param acquiredInfo An integer corresponding to a known acquired message.
+     * @param vendorCode An integer representing a custom vendor-specific message. Ignored unless
+     *  {@code acquiredInfo} is {@code FACE_ACQUIRED_VENDOR}.
+     * @param pan The horizontal pan of the detected face. Values in the range [-1, 1] indicate a
+     *  good capture.
+     * @param tilt The vertical tilt of the detected face. Values in the range [-1, 1] indicate a
+     *  good capture.
+     * @param distance The distance of the detected face from the device. Values in the range
+     *  [-1, 1] indicate a good capture.
+     * @param isCancellable Whether the ongoing face operation should be canceled.
+     */
+    public FaceDataFrame(
+            int acquiredInfo,
+            int vendorCode,
+            float pan,
+            float tilt,
+            float distance,
+            boolean isCancellable) {
+        mAcquiredInfo = acquiredInfo;
+        mVendorCode = vendorCode;
+        mPan = pan;
+        mTilt = tilt;
+        mDistance = distance;
+        mIsCancellable = isCancellable;
+    }
+
+    /**
+     * @return An integer corresponding to a known acquired message.
+     *
+     * @see android.hardware.biometrics.BiometricFaceConstants
+     */
+    public int getAcquiredInfo() {
+        return mAcquiredInfo;
+    }
+
+    /**
+     * @return An integer representing a custom vendor-specific message. Ignored unless
+     * {@code acquiredInfo} is {@link
+     * android.hardware.biometrics.BiometricFaceConstants#FACE_ACQUIRED_VENDOR}.
+     *
+     * @see android.hardware.biometrics.BiometricFaceConstants
+     */
+    public int getVendorCode() {
+        return mVendorCode;
+    }
+
+    /**
+     * @return The horizontal pan of the detected face. Values in the range [-1, 1] indicate a good
+     * capture.
+     */
+    public float getPan() {
+        return mPan;
+    }
+
+    /**
+     * @return The vertical tilt of the detected face. Values in the range [-1, 1] indicate a good
+     * capture.
+     */
+    public float getTilt() {
+        return mTilt;
+    }
+
+    /**
+     * @return The distance of the detected face from the device. Values in the range [-1, 1]
+     * indicate a good capture.
+     */
+    public float getDistance() {
+        return mDistance;
+    }
+
+    /**
+     * @return Whether the ongoing face operation should be canceled.
+     */
+    public boolean isCancellable() {
+        return mIsCancellable;
+    }
+
+    private FaceDataFrame(@NonNull Parcel source) {
+        mAcquiredInfo = source.readInt();
+        mVendorCode = source.readInt();
+        mPan = source.readFloat();
+        mTilt = source.readFloat();
+        mDistance = source.readFloat();
+        mIsCancellable = source.readBoolean();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mAcquiredInfo);
+        dest.writeInt(mVendorCode);
+        dest.writeFloat(mPan);
+        dest.writeFloat(mTilt);
+        dest.writeFloat(mDistance);
+        dest.writeBoolean(mIsCancellable);
+    }
+
+    public static final Creator<FaceDataFrame> CREATOR = new Creator<FaceDataFrame>() {
+        @Override
+        public FaceDataFrame createFromParcel(Parcel source) {
+            return new FaceDataFrame(source);
+        }
+
+        @Override
+        public FaceDataFrame[] newArray(int size) {
+            return new FaceDataFrame[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollCell.java b/core/java/android/hardware/face/FaceEnrollCell.java
new file mode 100644
index 0000000..8415419
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollCell.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A matrix cell, corresponding to a desired face image, that may be captured during enrollment.
+ *
+ * @hide
+ */
+public final class FaceEnrollCell implements Parcelable {
+    private final int mX;
+    private final int mY;
+    private final int mZ;
+
+    /**
+     * A matrix cell, corresponding to a desired face image, that may be captured during enrollment.
+     *
+     * @param x The horizontal coordinate of this cell.
+     * @param y The vertical coordinate of this cell.
+     * @param z The depth coordinate of this cell.
+     */
+    public FaceEnrollCell(int x, int y, int z) {
+        mX = x;
+        mY = y;
+        mZ = z;
+    }
+
+    /**
+     * @return The horizontal coordinate of this cell.
+     */
+    public int getX() {
+        return mX;
+    }
+
+    /**
+     * @return The vertical coordinate of this cell.
+     */
+    public int getY() {
+        return mY;
+    }
+
+    /**
+     * @return The depth coordinate of this cell.
+     */
+    public int getZ() {
+        return mZ;
+    }
+
+    private FaceEnrollCell(@NonNull Parcel source) {
+        mX = source.readInt();
+        mY = source.readInt();
+        mZ = source.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mX);
+        dest.writeInt(mY);
+        dest.writeInt(mZ);
+    }
+
+    public static final Creator<FaceEnrollCell> CREATOR = new Creator<FaceEnrollCell>() {
+        @Override
+        public FaceEnrollCell createFromParcel(Parcel source) {
+            return new FaceEnrollCell(source);
+        }
+
+        @Override
+        public FaceEnrollCell[] newArray(int size) {
+            return new FaceEnrollCell[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollFrame.java b/core/java/android/hardware/face/FaceEnrollFrame.java
new file mode 100644
index 0000000..551139d
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollFrame.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2021 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data model for a frame captured during face enrollment.
+ *
+ * @hide
+ */
+public final class FaceEnrollFrame implements Parcelable {
+    @Nullable private final FaceEnrollCell mCell;
+    @FaceEnrollStage private final int mStage;
+    @NonNull private final FaceDataFrame mData;
+
+    /**
+     * Data model for a frame captured during face enrollment.
+     *
+     * @param cell The cell captured during this frame of enrollment, if any.
+     * @param stage An integer representing the current stage of enrollment.
+     * @param data Information about the current frame.
+     */
+    public FaceEnrollFrame(
+            @Nullable FaceEnrollCell cell,
+            @FaceEnrollStage int stage,
+            @NonNull FaceDataFrame data) {
+        mCell = cell;
+        mStage = stage;
+        mData = data;
+    }
+
+    /**
+     * @return The cell captured during this frame of enrollment, if any.
+     */
+    @Nullable
+    public FaceEnrollCell getCell() {
+        return mCell;
+    }
+
+    /**
+     * @return An integer representing the current stage of enrollment.
+     */
+    @FaceEnrollStage
+    public int getStage() {
+        return mStage;
+    }
+
+    /**
+     * @return Information about the current frame.
+     */
+    @NonNull
+    public FaceDataFrame getData() {
+        return mData;
+    }
+
+    private FaceEnrollFrame(@NonNull Parcel source) {
+        mCell = source.readParcelable(FaceEnrollCell.class.getClassLoader());
+        mStage = source.readInt();
+        mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mCell, flags);
+        dest.writeInt(mStage);
+        dest.writeParcelable(mData, flags);
+    }
+
+    public static final Creator<FaceEnrollFrame> CREATOR = new Creator<FaceEnrollFrame>() {
+        @Override
+        public FaceEnrollFrame createFromParcel(Parcel source) {
+            return new FaceEnrollFrame(source);
+        }
+
+        @Override
+        public FaceEnrollFrame[] newArray(int size) {
+            return new FaceEnrollFrame[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollStage.java b/core/java/android/hardware/face/FaceEnrollStage.java
new file mode 100644
index 0000000..03dba55
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollStage.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 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.hardware.face;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A stage that may occur during face enrollment.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.SOURCE)
+@IntDef({
+    FaceEnrollStage.FIRST_FRAME_RECEIVED,
+    FaceEnrollStage.WAITING_FOR_CENTERING,
+    FaceEnrollStage.HOLD_STILL_IN_CENTER,
+    FaceEnrollStage.ENROLLING_MOVEMENT_1,
+    FaceEnrollStage.ENROLLING_MOVEMENT_2,
+    FaceEnrollStage.ENROLLMENT_FINISHED
+})
+public @interface FaceEnrollStage {
+    /**
+     * Enrollment has just begun. No action is needed from the user yet.
+     */
+    int FIRST_FRAME_RECEIVED = 0;
+
+    /**
+     * The user must center their face in the frame.
+     */
+    int WAITING_FOR_CENTERING = 1;
+
+    /**
+     * The user must keep their face centered in the frame.
+     */
+    int HOLD_STILL_IN_CENTER = 2;
+
+    /**
+     * The user must follow a first set of movement instructions.
+     */
+    int ENROLLING_MOVEMENT_1 = 3;
+
+    /**
+     * The user must follow a second set of movement instructions.
+     */
+    int ENROLLING_MOVEMENT_2 = 4;
+
+    /**
+     * Enrollment has completed. No more action is needed from the user.
+     */
+    int ENROLLMENT_FINISHED = 5;
+}
diff --git a/core/java/android/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS
index e6a04da..d7db7c7 100644
--- a/core/java/android/inputmethodservice/OWNERS
+++ b/core/java/android/inputmethodservice/OWNERS
@@ -2,3 +2,5 @@
 set noparent
 
 include /services/core/java/com/android/server/inputmethod/OWNERS
+
+per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 84a2acc..b016ed6 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -82,4 +82,5 @@
 
     boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
     boolean isUidRestrictedOnMeteredNetworks(int uid);
+    boolean checkUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted);
 }
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index ed169e7..3e6237d 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -464,6 +464,31 @@
     }
 
     /**
+     * Figure out if networking is blocked for a given set of conditions.
+     *
+     * This is used by ConnectivityService via passing stale copies of conditions, so it must not
+     * take any locks.
+     *
+     * @param uid The target uid.
+     * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+     * @param isNetworkMetered True if the network is metered.
+     * @param isBackgroundRestricted True if data saver is enabled.
+     *
+     * @return true if networking is blocked for the UID under the specified conditions.
+     *
+     * @hide
+     */
+    public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+            boolean isNetworkMetered, boolean isBackgroundRestricted) {
+        try {
+            return mService.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+                    isBackgroundRestricted);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Check that the given uid is restricted from doing networking on metered networks.
      *
      * @param uid The target uid.
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 4cd62eb..f2b466d 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -70,17 +70,6 @@
     public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999;
 
     /**
-     * Modeled power components are used for testing only.  They are returned if the
-     * {@link BatteryUsageStatsQuery#FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED} is set.
-     * The modeled power components are retrieved with {@link #getConsumedPowerForCustomComponent}.
-     * The ID of a modeled power component is calculated as
-     * (FIRST_MODELED_POWER_COMPONENT_ID + powerComponentId), e.g.
-     * FIRST_MODELED_POWER_COMPONENT_ID + POWER_COMPONENT_CPU.
-     */
-    public static final int FIRST_MODELED_POWER_COMPONENT_ID = 10000;
-    public static final int LAST_MODELED_POWER_COMPONENT_ID = 19999;
-
-    /**
      * Time usage component, describing the particular part of the system
      * that was used for the corresponding amount of time.
      *
@@ -182,10 +171,9 @@
     protected abstract static class BaseBuilder<T extends BaseBuilder<?>> {
         final PowerComponents.Builder mPowerComponentsBuilder;
 
-        public BaseBuilder(int customPowerComponentCount, int customTimeComponentCount,
-                boolean includeModeledComponents) {
+        public BaseBuilder(int customPowerComponentCount, int customTimeComponentCount) {
             mPowerComponentsBuilder = new PowerComponents.Builder(customPowerComponentCount,
-                    customTimeComponentCount, includeModeledComponents);
+                    customTimeComponentCount);
         }
 
         /**
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b846142..cc86a60 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1556,8 +1556,7 @@
         public int statSoftIrqTime;
         public int statIdlTime;
 
-        // Platform-level low power state stats
-        public String statPlatformIdleState;
+        // Low power state stats
         public String statSubsystemPowerState;
 
         public HistoryStepDetails() {
@@ -1589,7 +1588,6 @@
             out.writeInt(statIrqTime);
             out.writeInt(statSoftIrqTime);
             out.writeInt(statIdlTime);
-            out.writeString(statPlatformIdleState);
             out.writeString(statSubsystemPowerState);
         }
 
@@ -1611,7 +1609,6 @@
             statIrqTime = in.readInt();
             statSoftIrqTime = in.readInt();
             statIdlTime = in.readInt();
-            statPlatformIdleState = in.readString();
             statSubsystemPowerState = in.readString();
         }
     }
@@ -1656,6 +1653,7 @@
         public byte batteryPlugType;
 
         public short batteryTemperature;
+        // Battery voltage in millivolts (mV).
         @UnsupportedAppUsage
         public char batteryVoltage;
 
@@ -6599,9 +6597,6 @@
                             item.append(sb);
                             item.append(")");
                         }
-                        item.append(", PlatformIdleStat ");
-                        item.append(rec.stepDetails.statPlatformIdleState);
-                        item.append("\n");
 
                         item.append(", SubsystemPowerState ");
                         item.append(rec.stepDetails.statSubsystemPowerState);
@@ -6639,12 +6634,6 @@
                         item.append(',');
                         item.append(rec.stepDetails.statIdlTime);
                         item.append(',');
-                        if (rec.stepDetails.statPlatformIdleState != null) {
-                            item.append(rec.stepDetails.statPlatformIdleState);
-                            if (rec.stepDetails.statSubsystemPowerState != null) {
-                                item.append(',');
-                            }
-                        }
 
                         if (rec.stepDetails.statSubsystemPowerState != null) {
                             item.append(rec.stepDetails.statSubsystemPowerState);
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index f21a812..04e529e 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -32,6 +32,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 
 /**
  * This class provides an API surface for internal system components to report events that are
@@ -183,8 +184,20 @@
     @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
     @NonNull
     public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
+        return getBatteryUsageStats(List.of(query)).get(0);
+    }
+
+    /**
+     * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
+     * and per-UID basis.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+    @NonNull
+    public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
         try {
-            return mBatteryStats.getBatteryUsageStats(query);
+            return mBatteryStats.getBatteryUsageStats(queries);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index a6df87d..af8e8de 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -114,7 +114,6 @@
     public static final class Builder {
         private final int mCustomPowerComponentCount;
         private final int mCustomTimeComponentCount;
-        private final boolean mIncludeModeledComponents;
         private double mConsumedPower;
         private int mDischargePercentage;
         private final SparseArray<UidBatteryConsumer.Builder> mUidBatteryConsumerBuilders =
@@ -122,11 +121,9 @@
         private final SparseArray<SystemBatteryConsumer.Builder> mSystemBatteryConsumerBuilders =
                 new SparseArray<>();
 
-        public Builder(int customPowerComponentCount, int customTimeComponentCount,
-                boolean includeModeledComponents) {
+        public Builder(int customPowerComponentCount, int customTimeComponentCount) {
             mCustomPowerComponentCount = customPowerComponentCount;
             mCustomTimeComponentCount = customTimeComponentCount;
-            mIncludeModeledComponents = includeModeledComponents;
         }
 
         /**
@@ -169,7 +166,7 @@
             UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid);
             if (builder == null) {
                 builder = new UidBatteryConsumer.Builder(mCustomPowerComponentCount,
-                        mCustomTimeComponentCount, mIncludeModeledComponents, batteryStatsUid);
+                        mCustomTimeComponentCount, batteryStatsUid);
                 mUidBatteryConsumerBuilders.put(uid, builder);
             }
             return builder;
@@ -185,7 +182,7 @@
             SystemBatteryConsumer.Builder builder = mSystemBatteryConsumerBuilders.get(drainType);
             if (builder == null) {
                 builder = new SystemBatteryConsumer.Builder(mCustomPowerComponentCount,
-                        mCustomTimeComponentCount, mIncludeModeledComponents, drainType);
+                        mCustomTimeComponentCount, drainType);
                 mSystemBatteryConsumerBuilders.put(drainType, builder);
             }
             return builder;
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 9a00679..48e7389 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -38,18 +38,19 @@
      * @hide
      */
     @IntDef(flag = true, prefix = { "FLAG_BATTERY_USAGE_STATS_" }, value = {
-            FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED,
+            FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface BatteryUsageStatsFlags {}
 
     /**
-     * Indicates that modeled battery usage stats should be returned along with
-     * measured ones.
+     * Indicates that power estimations should be based on the usage time and
+     * average power constants provided in the PowerProfile, even if on-device power monitoring
+     * is available.
      *
      * @hide
      */
-    public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED = 1;
+    public static final int FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL = 1;
 
     private final int mFlags;
 
@@ -112,11 +113,13 @@
         }
 
         /**
-         * Requests to include modeled battery usage stats along with measured ones.
+         * Requests to return modeled battery usage stats only, even if on-device
+         * power monitoring data is available.
+         *
          * Should only be used for testing and debugging.
          */
-        public Builder includeModeled() {
-            mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED;
+        public Builder powerProfileModeledOnly() {
+            mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL;
             return this;
         }
     }
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 0185ba4..16d041a 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -547,7 +547,8 @@
         }
 
         try {
-            return transactNative(code, data, reply, flags);
+            boolean replyOwnsNative = (reply == null) ? false : reply.ownsNativeParcelObject();
+            return transactNative(code, data, reply, replyOwnsNative, flags);
         } finally {
             AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
 
@@ -572,7 +573,7 @@
      * Native implementation of transact() for proxies
      */
     public native boolean transactNative(int code, Parcel data, Parcel reply,
-            int flags) throws RemoteException;
+            boolean replyOwnsNativeParcelObject, int flags) throws RemoteException;
     /**
      * See {@link IBinder#linkToDeath(DeathRecipient, int)}
      */
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index fc65090..5f5a910 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3729,4 +3729,9 @@
     public long getBlobAshmemSize() {
         return nativeGetBlobAshmemSize(mNativePtr);
     }
+
+    /** @hide */
+    /*package*/ boolean ownsNativeParcelObject() {
+        return mOwnsNativeParcelObject;
+    }
 }
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index f681c4f..1337d55 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -33,11 +33,9 @@
     private final double[] mPowerComponents;
     private final long[] mTimeComponents;
     private final int mCustomPowerComponentCount;
-    private final int mModeledPowerComponentOffset;
 
     PowerComponents(@NonNull Builder builder) {
         mCustomPowerComponentCount = builder.mCustomPowerComponentCount;
-        mModeledPowerComponentOffset = builder.mModeledPowerComponentOffset;
         mPowerComponents = builder.mPowerComponents;
         mTimeComponents = builder.mTimeComponents;
         double totalPower = 0;
@@ -50,9 +48,6 @@
     PowerComponents(@NonNull Parcel source) {
         mTotalPowerConsumed = source.readDouble();
         mCustomPowerComponentCount = source.readInt();
-        mModeledPowerComponentOffset =
-                BatteryConsumer.POWER_COMPONENT_COUNT + mCustomPowerComponentCount
-                        - BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID;
         mPowerComponents = source.createDoubleArray();
         mTimeComponents = source.createLongArray();
     }
@@ -106,14 +101,6 @@
                 throw new IllegalArgumentException(
                         "Unsupported custom power component ID: " + componentId);
             }
-        } else if (componentId >= BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
-                && componentId < BatteryConsumer.LAST_MODELED_POWER_COMPONENT_ID) {
-            try {
-                return mPowerComponents[mModeledPowerComponentOffset + componentId];
-            } catch (ArrayIndexOutOfBoundsException e) {
-                throw new IllegalArgumentException(
-                        "Unsupported modeled power component ID: " + componentId);
-            }
         } else {
             throw new IllegalArgumentException(
                     "Unsupported custom power component ID: " + componentId);
@@ -165,20 +152,12 @@
         private final double[] mPowerComponents;
         private final int mCustomPowerComponentCount;
         private final long[] mTimeComponents;
-        private final int mModeledPowerComponentOffset;
 
-        Builder(int customPowerComponentCount, int customTimeComponentCount,
-                boolean includeModeledPowerComponents) {
+        Builder(int customPowerComponentCount, int customTimeComponentCount) {
             mCustomPowerComponentCount = customPowerComponentCount;
             int powerComponentCount =
                     BatteryConsumer.POWER_COMPONENT_COUNT + customPowerComponentCount;
-            if (includeModeledPowerComponents) {
-                powerComponentCount += BatteryConsumer.POWER_COMPONENT_COUNT;
-            }
             mPowerComponents = new double[powerComponentCount];
-            mModeledPowerComponentOffset =
-                    BatteryConsumer.POWER_COMPONENT_COUNT + mCustomPowerComponentCount
-                            - BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID;
             mTimeComponents =
                     new long[BatteryConsumer.TIME_COMPONENT_COUNT + customTimeComponentCount];
         }
@@ -222,14 +201,6 @@
                     throw new IllegalArgumentException(
                             "Unsupported custom power component ID: " + componentId);
                 }
-            } else if (componentId >= BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
-                    && componentId < BatteryConsumer.LAST_MODELED_POWER_COMPONENT_ID) {
-                try {
-                    mPowerComponents[mModeledPowerComponentOffset + componentId] = componentPower;
-                } catch (ArrayIndexOutOfBoundsException e) {
-                    throw new IllegalArgumentException(
-                            "Unsupported modeled power component ID: " + componentId);
-                }
             } else {
                 throw new IllegalArgumentException(
                         "Unsupported custom power component ID: " + componentId);
diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java
index 49bf084..fc4aa93 100644
--- a/core/java/android/os/SystemBatteryConsumer.java
+++ b/core/java/android/os/SystemBatteryConsumer.java
@@ -123,8 +123,8 @@
         private List<UidBatteryConsumer.Builder> mUidBatteryConsumers;
 
         Builder(int customPowerComponentCount, int customTimeComponentCount,
-                boolean includeModeledComponents, @DrainType int drainType) {
-            super(customPowerComponentCount, customTimeComponentCount, includeModeledComponents);
+                @DrainType int drainType) {
+            super(customPowerComponentCount, customTimeComponentCount);
             mDrainType = drainType;
         }
 
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 3161766..3ffaa9e 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -98,8 +98,8 @@
         private boolean mSystemComponent;
 
         public Builder(int customPowerComponentCount, int customTimeComponentCount,
-                boolean includeModeledComponents, BatteryStats.Uid batteryStatsUid) {
-            super(customPowerComponentCount, customTimeComponentCount, includeModeledComponents);
+                BatteryStats.Uid batteryStatsUid) {
+            super(customPowerComponentCount, customTimeComponentCount);
             mBatteryStatsUid = batteryStatsUid;
             mUid = batteryStatsUid.getUid();
         }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 77183ac..ea1ce37 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1694,10 +1694,13 @@
     }
 
     /**
-     * @hide
-     * @return Whether the device is running in a headless system user mode. It means the headless
-     * user (system user) runs system services and system UI, but is not associated with any real
-     * person. Secondary users can be created to be associated with real person.
+     * Checks whether the device is running in a headless system user mode.
+     *
+     * <p>Headless system user mode means the {@link #isSystemUser() system user} runs system
+     * services and some system UI, but it is not associated with any real person and additional
+     * users must be created to be associated with real persons.
+     *
+     * @return whether the device is running in a headless system user mode.
      */
     public static boolean isHeadlessSystemUserMode() {
         return RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER;
diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS
index cb1509a..cb06515 100644
--- a/core/java/android/provider/OWNERS
+++ b/core/java/android/provider/OWNERS
@@ -1,5 +1,6 @@
 per-file *BlockedNumber* = file:/telephony/OWNERS
 per-file *Telephony* = file:/telephony/OWNERS
+per-file *SimPhonebook* = file:/telephony/OWNERS
 
 per-file *CallLog* = file:platform/packages/providers/ContactsProvider:/OWNERS
 per-file *Contacts* = file:platform/packages/providers/ContactsProvider:/OWNERS
diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java
new file mode 100644
index 0000000..2efc212
--- /dev/null
+++ b/core/java/android/provider/SimPhonebookContract.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2020 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.provider;
+
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN_PATH_SEGMENT;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The contract between the provider of contact records on the device's SIM cards and applications.
+ * Contains definitions of the supported URIs and columns.
+ *
+ * <p>This content provider does not support any of the QUERY_ARG_SQL* bundle arguments. An
+ * IllegalArgumentException will be thrown if these are included.
+ */
+public final class SimPhonebookContract {
+
+    /** The authority for the SIM phonebook provider. */
+    public static final String AUTHORITY = "com.android.simphonebook";
+    /** The content:// style uri to the authority for the SIM phonebook provider. */
+    @NonNull
+    public static final Uri AUTHORITY_URI = Uri.parse("content://com.android.simphonebook");
+    /**
+     * The Uri path element used to indicate that the following path segment is a subscription ID
+     * for the SIM card that will be operated on.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
+
+    private SimPhonebookContract() {
+    }
+
+    /**
+     * Returns the Uri path segment used to reference the specified elementary file type for Uris
+     * returned by this API.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static String getEfUriPath(@ElementaryFiles.EfType int efType) {
+        switch (efType) {
+            case EF_ADN:
+                return EF_ADN_PATH_SEGMENT;
+            case EF_FDN:
+                return EF_FDN_PATH_SEGMENT;
+            case EF_SDN:
+                return EF_SDN_PATH_SEGMENT;
+            default:
+                throw new IllegalArgumentException("Unsupported EfType " + efType);
+        }
+    }
+
+    /** Constants for the contact records on a SIM card. */
+    public static final class SimRecords {
+
+        /**
+         * The subscription ID of the SIM the record is from.
+         *
+         * @see SubscriptionInfo#getSubscriptionId()
+         */
+        public static final String SUBSCRIPTION_ID = "subscription_id";
+        /**
+         * The type of the elementary file the record is from.
+         *
+         * @see ElementaryFiles#EF_ADN
+         * @see ElementaryFiles#EF_FDN
+         * @see ElementaryFiles#EF_SDN
+         */
+        public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
+        /**
+         * The 1-based offset of the record in the elementary file that contains it.
+         *
+         * <p>This can be used to access individual SIM records by appending it to the
+         * elementary file URIs but it is not like a normal database ID because it is not
+         * auto-incrementing and it is not unique across SIM cards or elementary files. Hence, care
+         * should be taken when using it to ensure that it is applied to the correct SIM and EF.
+         *
+         * @see #getItemUri(int, int, int)
+         */
+        public static final String RECORD_NUMBER = "record_number";
+        /**
+         * The name for this record.
+         *
+         * <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
+         * exceeds the maximum supported length or contains unsupported characters.
+         * {@link #validateName(ContentResolver, int, int, String)} )} can be used to
+         * check whether the name is supported.
+         *
+         * @see ElementaryFiles#NAME_MAX_LENGTH
+         * @see #validateName(ContentResolver, int, int, String) )
+         */
+        public static final String NAME = "name";
+        /**
+         * The phone number for this record.
+         *
+         * <p>Only dialable characters are supported.
+         *
+         * <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
+         * exceeds the maximum supported length or contains unsupported characters.
+         *
+         * @see ElementaryFiles#PHONE_NUMBER_MAX_LENGTH
+         * @see android.telephony.PhoneNumberUtils#isDialable(char)
+         */
+        public static final String PHONE_NUMBER = "phone_number";
+
+        /** The MIME type of a CONTENT_URI subdirectory of a single SIM record. */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
+        /** The MIME type of CONTENT_URI providing a directory of SIM records. */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
+
+        /**
+         * The path segment that is appended to {@link #getContentUri(int, int)} which indicates
+         * that the following path segment contains a name to be validated.
+         *
+         * @hide
+         * @see #validateName(ContentResolver, int, int, String)
+         */
+        @SystemApi
+        public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
+
+        /**
+         * The key for a cursor extra that contains the result of a validate name query.
+         *
+         * @hide
+         * @see #validateName(ContentResolver, int, int, String)
+         */
+        @SystemApi
+        public static final String EXTRA_NAME_VALIDATION_RESULT =
+                "android.provider.extra.NAME_VALIDATION_RESULT";
+
+
+        /**
+         * Key for the PIN2 needed to modify FDN record that should be passed in the Bundle
+         * passed to {@link ContentResolver#insert(Uri, ContentValues, Bundle)},
+         * {@link ContentResolver#update(Uri, ContentValues, Bundle)}
+         * and {@link ContentResolver#delete(Uri, Bundle)}.
+         *
+         * <p>Modifying FDN records also requires either
+         * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or
+         * {@link TelephonyManager#hasCarrierPrivileges()}
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
+
+        private SimRecords() {
+        }
+
+        /**
+         * Returns the content Uri for the specified elementary file on the specified SIM.
+         *
+         * <p>When queried this Uri will return all of the contact records in the specified
+         * elementary file on the specified SIM. The available subscriptionIds and efTypes can
+         * be discovered by querying {@link ElementaryFiles#CONTENT_URI}.
+         *
+         * <p>If a SIM with the provided subscription ID does not exist or the SIM with the provided
+         * subscription ID doesn't support the specified entity file then queries will return
+         * and empty cursor and inserts will throw an {@link IllegalArgumentException}
+         *
+         * @param subscriptionId the subscriptionId of the SIM card that this Uri will reference
+         * @param efType         the elementary file on the SIM that this Uri will reference
+         * @see ElementaryFiles#EF_ADN
+         * @see ElementaryFiles#EF_FDN
+         * @see ElementaryFiles#EF_SDN
+         */
+        @NonNull
+        public static Uri getContentUri(int subscriptionId, @ElementaryFiles.EfType int efType) {
+            return buildContentUri(subscriptionId, efType).build();
+        }
+
+        /**
+         * Content Uri for the specific SIM record with the provided {@link #RECORD_NUMBER}.
+         *
+         * <p>When queried this will return the record identified by the provided arguments.
+         *
+         * <p>For a non-existent record:
+         * <ul>
+         *     <li>query will return an empty cursor</li>
+         *     <li>update will return 0</li>
+         *     <li>delete will return 0</li>
+         * </ul>
+         *
+         * @param subscriptionId the subscription ID of the SIM containing the record. If no SIM
+         *                       with this subscription ID exists then it will be treated as a
+         *                       non-existent record
+         * @param efType         the elementary file type containing the record. If the specified
+         *                       SIM doesn't support this elementary file then it will be treated
+         *                       as a non-existent record.
+         * @param recordNumber   the record number of the record this Uri should reference. This
+         *                       must be greater than 0. If there is no record with this record
+         *                       number in the specified entity file then it will be treated as a
+         *                       non-existent record.
+         */
+        @NonNull
+        public static Uri getItemUri(
+                int subscriptionId, @ElementaryFiles.EfType int efType, int recordNumber) {
+            // Elementary file record indices are 1-based.
+            Preconditions.checkArgument(recordNumber > 0, "Invalid recordNumber");
+
+            return buildContentUri(subscriptionId, efType)
+                    .appendPath(String.valueOf(recordNumber))
+                    .build();
+        }
+
+        /**
+         * Validates a value that is being provided for the {@link #NAME} column.
+         *
+         * <p>The return value can be used to check if the name is valid. If it is not valid then
+         * inserts and updates to the specified elementary file that use the provided name value
+         * will throw an {@link IllegalArgumentException}.
+         *
+         * <p>If the specified SIM or elementary file don't exist then
+         * {@link NameValidationResult#getMaxEncodedLength()} will be zero and
+         * {@link NameValidationResult#isValid()} will return false.
+         */
+        @NonNull
+        @WorkerThread
+        public static NameValidationResult validateName(
+                @NonNull ContentResolver resolver, int subscriptionId,
+                @ElementaryFiles.EfType int efType,
+                @NonNull String name) {
+            Bundle queryArgs = new Bundle();
+            queryArgs.putString(SimRecords.NAME, name);
+            try (Cursor cursor =
+                         resolver.query(buildContentUri(subscriptionId, efType)
+                                 .appendPath(VALIDATE_NAME_PATH_SEGMENT)
+                                 .build(), null, queryArgs, null)) {
+                NameValidationResult result = cursor.getExtras()
+                        .getParcelable(EXTRA_NAME_VALIDATION_RESULT);
+                return result != null ? result : new NameValidationResult(name, "", 0, 0);
+            }
+        }
+
+        private static Uri.Builder buildContentUri(
+                int subscriptionId, @ElementaryFiles.EfType int efType) {
+            return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                    .authority(AUTHORITY)
+                    .appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
+                    .appendPath(String.valueOf(subscriptionId))
+                    .appendPath(getEfUriPath(efType));
+        }
+
+        /** Contains details about the validity of a value provided for the {@link #NAME} column. */
+        public static final class NameValidationResult implements Parcelable {
+
+            @NonNull
+            public static final Creator<NameValidationResult> CREATOR =
+                    new Creator<NameValidationResult>() {
+
+                        @Override
+                        public NameValidationResult createFromParcel(@NonNull Parcel in) {
+                            return new NameValidationResult(in);
+                        }
+
+                        @NonNull
+                        @Override
+                        public NameValidationResult[] newArray(int size) {
+                            return new NameValidationResult[size];
+                        }
+                    };
+
+            private final String mName;
+            private final String mSanitizedName;
+            private final int mEncodedLength;
+            private final int mMaxEncodedLength;
+
+            /** Creates a new instance from the provided values. */
+            public NameValidationResult(@NonNull String name, @NonNull String sanitizedName,
+                    int encodedLength, int maxEncodedLength) {
+                this.mName = Objects.requireNonNull(name);
+                this.mSanitizedName = Objects.requireNonNull(sanitizedName);
+                this.mEncodedLength = encodedLength;
+                this.mMaxEncodedLength = maxEncodedLength;
+            }
+
+            private NameValidationResult(Parcel in) {
+                this(in.readString(), in.readString(), in.readInt(), in.readInt());
+            }
+
+            /** Returns the original name that is being validated. */
+            @NonNull
+            public String getName() {
+                return mName;
+            }
+
+            /**
+             * Returns a sanitized copy of the original name with all unsupported characters
+             * replaced with spaces.
+             */
+            @NonNull
+            public String getSanitizedName() {
+                return mSanitizedName;
+            }
+
+            /**
+             * Returns whether the original name isValid.
+             *
+             * <p>If this returns false then inserts and updates using the name will throw an
+             * {@link IllegalArgumentException}
+             */
+            public boolean isValid() {
+                return mMaxEncodedLength > 0 && mEncodedLength <= mMaxEncodedLength
+                        && Objects.equals(
+                        mName, mSanitizedName);
+            }
+
+            /** Returns whether the character at the specified position is supported by the SIM. */
+            public boolean isSupportedCharacter(int position) {
+                return mName.charAt(position) == mSanitizedName.charAt(position);
+            }
+
+            /**
+             * Returns the number of bytes required to save the name.
+             *
+             * <p>This may be more than the number of characters in the name.
+             */
+            public int getEncodedLength() {
+                return mEncodedLength;
+            }
+
+            /**
+             * Returns the maximum number of bytes that are supported for the name.
+             *
+             * @see ElementaryFiles#NAME_MAX_LENGTH
+             */
+            public int getMaxEncodedLength() {
+                return mMaxEncodedLength;
+            }
+
+            @Override
+            public int describeContents() {
+                return 0;
+            }
+
+            @Override
+            public void writeToParcel(@NonNull Parcel dest, int flags) {
+                dest.writeString(mName);
+                dest.writeString(mSanitizedName);
+                dest.writeInt(mEncodedLength);
+                dest.writeInt(mMaxEncodedLength);
+            }
+        }
+    }
+
+    /** Constants for metadata about the elementary files of the SIM cards in the phone. */
+    public static final class ElementaryFiles {
+
+        /** {@link SubscriptionInfo#getSimSlotIndex()} of the SIM for this row. */
+        public static final String SLOT_INDEX = "slot_index";
+        /** {@link SubscriptionInfo#getSubscriptionId()} of the SIM for this row. */
+        public static final String SUBSCRIPTION_ID = "subscription_id";
+        /**
+         * The elementary file type for this row.
+         *
+         * @see ElementaryFiles#EF_ADN
+         * @see ElementaryFiles#EF_FDN
+         * @see ElementaryFiles#EF_SDN
+         */
+        public static final String EF_TYPE = "ef_type";
+        /** The maximum number of records supported by the elementary file. */
+        public static final String MAX_RECORDS = "max_records";
+        /** Count of the number of records that are currently stored in the elementary file. */
+        public static final String RECORD_COUNT = "record_count";
+        /** The maximum length supported for the name of a record in the elementary file. */
+        public static final String NAME_MAX_LENGTH = "name_max_length";
+        /**
+         * The maximum length supported for the phone number of a record in the elementary file.
+         */
+        public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
+
+        /**
+         * A value for an elementary file that is not recognized.
+         *
+         * <p>Generally this should be ignored. If new values are added then this will be used
+         * for apps that target SDKs where they aren't defined.
+         */
+        public static final int EF_UNKNOWN = 0;
+        /**
+         * Type for accessing records in the "abbreviated dialing number" (ADN) elementary file on
+         * the SIM.
+         *
+         * <p>ADN records are typically user created.
+         */
+        public static final int EF_ADN = 1;
+        /**
+         * Type for accessing records in the "fixed dialing number" (FDN) elementary file on the
+         * SIM.
+         *
+         * <p>FDN numbers are the numbers that are allowed to dialed for outbound calls when FDN is
+         * enabled.
+         *
+         * <p>FDN records cannot be modified by applications. Hence, insert, update and
+         * delete methods operating on this Uri will throw UnsupportedOperationException
+         */
+        public static final int EF_FDN = 2;
+        /**
+         * Type for accessing records in the "service dialing number" (SDN) elementary file on the
+         * SIM.
+         *
+         * <p>Typically SDNs are preset numbers provided by the carrier for common operations (e.g.
+         * voicemail, check balance, etc).
+         *
+         * <p>SDN records cannot be modified by applications. Hence, insert, update and delete
+         * methods operating on this Uri will throw UnsupportedOperationException
+         */
+        public static final int EF_SDN = 3;
+        /** @hide */
+        @SystemApi
+        public static final String EF_ADN_PATH_SEGMENT = "adn";
+        /** @hide */
+        @SystemApi
+        public static final String EF_FDN_PATH_SEGMENT = "fdn";
+        /** @hide */
+        @SystemApi
+        public static final String EF_SDN_PATH_SEGMENT = "sdn";
+        /** The MIME type of CONTENT_URI providing a directory of ADN-like elementary files. */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
+        /** The MIME type of a CONTENT_URI subdirectory of a single ADN-like elementary file. */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/sim-elementary-file";
+        /**
+         * The Uri path segment used to construct Uris for the metadata defined in this class.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
+
+        /** Content URI for the ADN-like elementary files available on the device. */
+        @NonNull
+        public static final Uri CONTENT_URI = AUTHORITY_URI
+                .buildUpon()
+                .appendPath(ELEMENTARY_FILES_PATH_SEGMENT).build();
+
+        private ElementaryFiles() {
+        }
+
+        /**
+         * Returns a content uri for a specific elementary file.
+         *
+         * <p>If a SIM with the specified subscriptionId is not present an exception will be thrown.
+         * If the SIM doesn't support the specified elementary file it will have a zero value for
+         * {@link #MAX_RECORDS}.
+         */
+        @NonNull
+        public static Uri getItemUri(int subscriptionId, @EfType int efType) {
+            return CONTENT_URI.buildUpon().appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
+                    .appendPath(String.valueOf(subscriptionId))
+                    .appendPath(getEfUriPath(efType))
+                    .build();
+        }
+
+        /**
+         * Annotation for the valid elementary file types.
+         *
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(
+                prefix = {"EF"},
+                value = {EF_UNKNOWN, EF_ADN, EF_FDN, EF_SDN})
+        public @interface EfType {
+        }
+    }
+}
diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
index 4ebaa96..ad49ffd 100644
--- a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
+++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
@@ -87,7 +87,9 @@
      * Implementation for wrapping the opaque blob used for resume-on-reboot prior to
      * reboot. The service should not assume any structure of the blob to be wrapped. The
      * implementation should wrap the opaque blob in a reasonable time or throw {@link IOException}
-     * if it's unable to complete the action.
+     * if it's unable to complete the action due to retry-able errors (e.g network errors)
+     * and {@link IllegalArgumentException} if {@code wrapBlob} fails due to fatal errors
+     * (e.g corrupted blob).
      *
      * @param blob             The opaque blob with size on the order of 100 bytes.
      * @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the
@@ -95,7 +97,8 @@
      *                         this function after expiration should
      *                         fail.
      * @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes.
-     * @throws IOException if the implementation is unable to wrap the blob successfully.
+     * @throws IOException if the implementation is unable to wrap the blob successfully due to
+     * retry-able errors.
      */
     @NonNull
     public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis)
@@ -106,12 +109,13 @@
      * operation would happen after reboot during direct boot mode (i.e before device is unlocked
      * for the first time). The implementation should unwrap the wrapped blob in a reasonable time
      * and returns the result or throw {@link IOException} if it's unable to complete the action
-     * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is
-     * stale.
+     * due to retry-able errors (e.g network error) and {@link IllegalArgumentException}
+     * if {@code unwrapBlob} fails due to fatal errors (e.g stale or corrupted blob).
      *
      * @param wrappedBlob The wrapped blob with size on the order of 100 bytes.
      * @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes.
-     * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully.
+     * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully
+     * due to retry-able errors.
      */
     @NonNull
     public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException;
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index 0123c36..a750b68 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -158,7 +158,7 @@
      * @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed
      * @param bytes number of bytes which need to be freed
      */
-    public void onFreeCacheRequested(@NonNull UUID volumeUuid, @BytesLong long bytes) {
+    public void onFreeCache(@NonNull UUID volumeUuid, @BytesLong long bytes) throws IOException {
         throw new UnsupportedOperationException("onFreeCacheRequested not implemented");
     }
 
@@ -202,7 +202,7 @@
                 RemoteCallback callback) {
             mHandler.post(() -> {
                 try {
-                    onFreeCacheRequested(StorageManager.convert(volumeUuid), bytes);
+                    onFreeCache(StorageManager.convert(volumeUuid), bytes);
                     sendResult(sessionId, null /* throwable */, callback);
                 } catch (Throwable t) {
                     sendResult(sessionId, t, callback);
diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
index f7710e6..ff03cc1 100644
--- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java
+++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
@@ -44,6 +45,7 @@
     private ServiceInfo mServiceInfo;
     private String mSessionService;
     private String mRecognitionService;
+    private String mHotwordDetectionService;
     private String mSettingsActivity;
     private boolean mSupportsAssist;
     private boolean mSupportsLaunchFromKeyguard;
@@ -133,6 +135,8 @@
                     false);
             mSupportsLocalInteraction = array.getBoolean(com.android.internal.
                     R.styleable.VoiceInteractionService_supportsLocalInteraction, false);
+            mHotwordDetectionService = array.getString(com.android.internal.R.styleable
+                    .VoiceInteractionService_hotwordDetectionService);
             array.recycle();
             if (mSessionService == null) {
                 mParseError = "No sessionService specified";
@@ -181,4 +185,9 @@
     public boolean getSupportsLocalInteraction() {
         return mSupportsLocalInteraction;
     }
+
+    @Nullable
+    public String getHotwordDetectionService() {
+        return mHotwordDetectionService;
+    }
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 507dc7a..82e0b4a 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -880,8 +880,8 @@
                         InputChannel inputChannel = new InputChannel();
 
                         if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
-                                mDisplay.getDisplayId(), mInsetsState, mWinFrames.frame,
-                                inputChannel, mInsetsState, mTempControls) < 0) {
+                                mDisplay.getDisplayId(), mInsetsState, inputChannel, mInsetsState,
+                                mTempControls) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
@@ -1494,6 +1494,16 @@
         private void doDetachEngine() {
             mActiveEngines.remove(mEngine);
             mEngine.detach();
+            // Some wallpapers will not trigger the rendering threads of the remaining engines even
+            // if they are visible, so we need to toggle the state to get their attention.
+            if (!mDetached.get()) {
+                for (Engine eng : mActiveEngines) {
+                    if (eng.mVisible) {
+                        eng.doVisibilityChanged(false);
+                        eng.doVisibilityChanged(true);
+                    }
+                }
+            }
         }
 
         @Override
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index b229212..790773f 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -65,7 +65,7 @@
         DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
 
         DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
-        DEFAULT_FLAGS.put("settings_silky_home", "false");
+        DEFAULT_FLAGS.put("settings_silky_home", "true");
         DEFAULT_FLAGS.put("settings_contextual_home", "false");
         DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
     }
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 86120d1..6718e93 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -16,6 +16,7 @@
 
 package android.util;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.ArrayUtils;
@@ -23,6 +24,8 @@
 
 import libcore.util.EmptyArray;
 
+import java.util.Objects;
+
 /**
  * <code>SparseArray</code> maps integers to Objects and, unlike a normal array of Objects,
  * its indices can contain gaps. <code>SparseArray</code> is intended to be more memory-efficient
@@ -505,4 +508,44 @@
         buffer.append('}');
         return buffer.toString();
     }
+
+    /**
+     * For backwards compatibility reasons, {@link Object#equals(Object)} cannot be implemented,
+     * so this serves as a manually invoked alternative.
+     */
+    public boolean contentEquals(@Nullable SparseArray<E> other) {
+        if (other == null) {
+            return false;
+        }
+
+        int size = size();
+        if (size != other.size()) {
+            return false;
+        }
+
+        for (int index = 0; index < size; index++) {
+            int key = keyAt(index);
+            if (!Objects.equals(valueAt(index), other.get(key))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * For backwards compatibility, {@link Object#hashCode()} cannot be implemented, so this serves
+     * as a manually invoked alternative.
+     */
+    public int contentHashCode() {
+        int hash = 0;
+        int size = size();
+        for (int index = 0; index < size; index++) {
+            int key = keyAt(index);
+            E value = valueAt(index);
+            hash = 31 * hash + Objects.hashCode(key);
+            hash = 31 * hash + Objects.hashCode(value);
+        }
+        return hash;
+    }
 }
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4168064..0ba1dfe 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -25,8 +25,8 @@
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.KeyguardManager;
-import android.app.WindowConfiguration;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -59,8 +59,12 @@
  * an application window, excluding the system decorations.  The application display area may
  * be smaller than the real display area because the system subtracts the space needed
  * for decor elements such as the status bar.  Use {@link WindowMetrics#getBounds()} to query the
- * application window bounds. Generally, use {@link WindowManager#getCurrentWindowMetrics()} to
- * query the metrics and perform UI-related actions.</li>
+ * application window bounds.</li>
+ * <li>The real display area specifies the part of the display that contains content
+ * including the system decorations.  Even so, the real display area may be smaller than the
+ * physical size of the display if the window manager is emulating a smaller display
+ * using (adb shell wm size).  Use the following methods to query the
+ * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li>
  * </ul>
  * </p><p>
  * A logical display does not necessarily represent a particular physical display device
@@ -673,9 +677,9 @@
     @UnsupportedAppUsage
     public DisplayAdjustments getDisplayAdjustments() {
         if (mResources != null) {
-            final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments();
-            if (!mDisplayAdjustments.equals(currentAdjustments)) {
-                mDisplayAdjustments = new DisplayAdjustments(currentAdjustments);
+            final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
+            if (!mDisplayAdjustments.equals(currentAdjustements)) {
+                mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
             }
         }
 
@@ -1213,34 +1217,30 @@
     }
 
     /**
-     * Provides the largest {@link Point outSize} an app may expect in the current system state,
-     * without subtracting any window decor.
+     * Gets the real size of the display without subtracting any window decor or
+     * applying any compatibility scale factors.
      * <p>
-     * The size describes the largest potential area the window might occupy. The size is adjusted
-     * based on the current rotation of the display.
+     * The size is adjusted based on the current rotation of the display.
      * </p><p>
      * The real size may be smaller than the physical size of the screen when the
      * window manager is emulating a smaller display (using adb shell wm size).
-     * </p>
+     * </p><p>
+     * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()}
+     * report the same bounds except that certain areas of the display may not be available to
+     * windows created in the {@link WindowManager}'s {@link Context}.
+     *
+     * For example, imagine a device which has a multi-task mode that limits windows to half of the
+     * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the
+     * bounds of the screen half where the window is located, while {@link #getRealSize(Point)}
+     * still reports the bounds of the whole display.
      *
      * @param outSize Set to the real size of the display.
+     *
+     * @see WindowManager#getMaximumWindowMetrics()
      */
     public void getRealSize(Point outSize) {
         synchronized (this) {
             updateDisplayInfoLocked();
-            if (shouldReportMaxBounds()) {
-                final Rect bounds = mResources.getConfiguration()
-                        .windowConfiguration.getMaxBounds();
-                outSize.x = bounds.width();
-                outSize.y = bounds.height();
-                if (DEBUG) {
-                    Log.d(TAG, "getRealSize determined from max bounds: " + outSize
-                            + " for uid " + Process.myUid());
-                }
-                // Skip adjusting by fixed rotation, since if it is necessary, the configuration
-                // should already reflect the expected rotation.
-                return;
-            }
             outSize.x = mDisplayInfo.logicalWidth;
             outSize.y = mDisplayInfo.logicalHeight;
             if (mMayAdjustByFixedRotation) {
@@ -1250,11 +1250,9 @@
     }
 
     /**
-     * Provides the largest {@link DisplayMetrics outMetrics} an app may expect in the current
-     * system state, without subtracting any window decor.
+     * Gets display metrics based on the real size of this display.
      * <p>
-     * The size describes the largest potential area the window might occupy. The size is adjusted
-     * based on the current rotation of the display.
+     * The size is adjusted based on the current rotation of the display.
      * </p><p>
      * The real size may be smaller than the physical size of the screen when the
      * window manager is emulating a smaller display (using adb shell wm size).
@@ -1265,18 +1263,6 @@
     public void getRealMetrics(DisplayMetrics outMetrics) {
         synchronized (this) {
             updateDisplayInfoLocked();
-            if (shouldReportMaxBounds()) {
-                mDisplayInfo.getMaxBoundsMetrics(outMetrics,
-                        CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
-                        mResources.getConfiguration());
-                if (DEBUG) {
-                    Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics
-                            + " for uid " + Process.myUid());
-                }
-                // Skip adjusting by fixed rotation, since if it is necessary, the configuration
-                // should already reflect the expected rotation.
-                return;
-            }
             mDisplayInfo.getLogicalMetrics(outMetrics,
                     CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
             if (mMayAdjustByFixedRotation) {
@@ -1286,20 +1272,6 @@
     }
 
     /**
-     * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the
-     * display dimensions. The max bounds field may be smaller than the logical dimensions
-     * when apps need to be sandboxed.
-     * @return {@code true} when max bounds should be applied.
-     */
-    private boolean shouldReportMaxBounds() {
-        if (mResources == null) {
-            return false;
-        }
-        final Configuration config = mResources.getConfiguration();
-        return config != null && !config.windowConfiguration.getMaxBounds().isEmpty();
-    }
-
-    /**
      * Gets the state of the display, such as whether it is on or off.
      *
      * @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8a44504..2a00b5a 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -24,7 +24,6 @@
 import static android.view.DisplayInfoProto.NAME;
 
 import android.annotation.Nullable;
-import android.app.WindowConfiguration;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -616,29 +615,11 @@
         getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
     }
 
-    /**
-     * Populates {@code outMetrics} with details of the logical display. Bounds are limited
-     * by the logical size of the display.
-     *
-     * @param outMetrics the {@link DisplayMetrics} to be populated
-     * @param compatInfo the {@link CompatibilityInfo} to be applied
-     * @param configuration the {@link Configuration}
-     */
     public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
             Configuration configuration) {
         getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
     }
 
-    /**
-     * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from
-     * {@link WindowConfiguration#getMaxBounds()}
-     */
-    public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
-            Configuration configuration) {
-        Rect bounds = configuration.windowConfiguration.getMaxBounds();
-        getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
-    }
-
     public int getNaturalWidth() {
         return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
                 logicalWidth : logicalHeight;
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index c2566cc..5937499 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -149,7 +149,7 @@
      * <p>
      * The time value that was used in all the vsync listeners and drawing for
      * the frame (Choreographer frame callbacks, animations,
-     * {@link View#getDrawingTime()}, etc…)
+     * {@link View#getDrawingTime()}, etc.)
      * </p>
      */
     public static final int VSYNC_TIMESTAMP = 11;
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
index 423e23d..1f64fb8 100644
--- a/core/java/android/view/IRemoteAnimationRunner.aidl
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -30,11 +30,15 @@
     /**
      * Called when the process needs to start the remote animation.
      *
+     * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
      * @param apps The list of apps to animate.
+     * @param wallpapers The list of wallpapers to animate.
+     * @param nonApps The list of non-app windows such as Bubbles to animate.
      * @param finishedCallback The callback to invoke when the animation is finished.
      */
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
-    void onAnimationStart(in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
+    void onAnimationStart(int transit, in RemoteAnimationTarget[] apps,
+            in RemoteAnimationTarget[] wallpapers, in RemoteAnimationTarget[] nonApps,
             in IRemoteAnimationFinishedCallback finishedCallback);
 
     /**
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7843411..ae8afca 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -117,7 +117,7 @@
     // These can only be called when holding the MANAGE_APP_TOKENS permission.
     void setEventDispatching(boolean enabled);
 
-    /** @return {@code true} if this binder is a registered window token. */
+    /** Returns {@code true} if this binder is a registered window token. */
     boolean isWindowToken(in IBinder binder);
     /**
      * Adds window token for a given type.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7b15f52..990b7bd 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -47,11 +47,11 @@
 interface IWindowSession {
     int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, in InsetsState requestedVisibility,
-            out Rect outFrame, out InputChannel outInputChannel, out InsetsState insetsState,
+            out InputChannel outInputChannel, out InsetsState insetsState,
             out InsetsSourceControl[] activeControls);
     int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, in int userId,
-            in InsetsState requestedVisibility, out Rect outFrame, out InputChannel outInputChannel,
+            in InsetsState requestedVisibility, out InputChannel outInputChannel,
             out InsetsState insetsState, out InsetsSourceControl[] activeControls);
     int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out InsetsState insetsState);
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index bc03222..59e4931 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -455,7 +455,6 @@
      * Called by native code
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @VisibleForTesting
     public InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
             int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 0939336..6a34a15 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -111,6 +111,9 @@
         mControl = new InsetsAnimationControlImpl(controls, frame, state, listener,
                 types, mCallbacks, durationMs, interpolator, animationType, translator);
         InsetsAnimationThread.getHandler().post(() -> {
+            if (mControl.isCancelled()) {
+                return;
+            }
             Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
                     "InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types);
             listener.onReady(mControl, types);
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index fe6b6e4..219190f 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -604,13 +604,13 @@
                 return Type.CAPTION_BAR;
             case ITYPE_IME:
                 return Type.IME;
-            case ITYPE_TOP_GESTURES:
-            case ITYPE_BOTTOM_GESTURES:
             case ITYPE_TOP_MANDATORY_GESTURES:
             case ITYPE_BOTTOM_MANDATORY_GESTURES:
             case ITYPE_LEFT_MANDATORY_GESTURES:
             case ITYPE_RIGHT_MANDATORY_GESTURES:
                 return Type.MANDATORY_SYSTEM_GESTURES;
+            case ITYPE_TOP_GESTURES:
+            case ITYPE_BOTTOM_GESTURES:
             case ITYPE_LEFT_GESTURES:
             case ITYPE_RIGHT_GESTURES:
                 return Type.SYSTEM_GESTURES;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index acd2507..98b4acd 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -188,8 +188,6 @@
             IBinder displayToken, int mode);
     private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject,
             long barrierObject, long frame);
-    private static native void nativeReparentChildren(long transactionObj, long nativeObject,
-            long newParentObject);
     private static native void nativeReparent(long transactionObj, long nativeObject,
             long newParentNativeObject);
 
@@ -2970,15 +2968,6 @@
         }
 
         /**
-         * @hide
-         */
-        public Transaction reparentChildren(SurfaceControl sc, SurfaceControl newParent) {
-            checkPreconditions(sc);
-            nativeReparentChildren(mNativeObject, sc.mNativeObject, newParent.mNativeObject);
-            return this;
-        }
-
-        /**
          * Re-parents a given layer to a new parent. Children inherit transform (position, scaling)
          * crop, visibility, and Z-ordering from their parents, as if the children were pixels within the
          * parent Surface.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f603ef7..70ec2d4 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -877,6 +877,10 @@
 
         synchronized (mSurfaceControlLock) {
             mSurface.release();
+            if (mBlastBufferQueue != null) {
+                mBlastBufferQueue.destroy();
+                mBlastBufferQueue = null;
+            }
 
             if (mRtHandlingPositionUpdates) {
                 mRtReleaseSurfaces = true;
@@ -901,10 +905,6 @@
             transaction.remove(mBlastSurfaceControl);
             mBlastSurfaceControl = null;
         }
-        if (mBlastBufferQueue != null) {
-            mBlastBufferQueue.destroy();
-            mBlastBufferQueue = null;
-        }
     }
 
     private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5e3599d..036a703 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -138,6 +138,7 @@
 import android.os.UserHandle;
 import android.sysprop.DisplayProperties;
 import android.util.AndroidRuntimeException;
+import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -157,6 +158,7 @@
 import android.view.View.FocusDirection;
 import android.view.View.MeasureSpec;
 import android.view.Window.OnContentApplyWindowInsetsListener;
+import android.view.WindowInsets.Side.InsetsSide;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -313,7 +315,7 @@
      * In that case we receive a call back from {@link ActivityThread} and this flag is used to
      * preserve the initial value.
      *
-     * @see #performConfigurationChange(Configuration, Configuration, boolean, int)
+     * @see #performConfigurationChange(MergedConfiguration, boolean, int)
      */
     private boolean mForceNextConfigUpdate;
 
@@ -926,6 +928,33 @@
         }
     }
 
+    // TODO(b/161810301): Make this private after window layout is moved to the client side.
+    public static void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state,
+            Rect displayFrame, Rect outBounds) {
+        final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
+        final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
+        final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
+        final Rect df = displayFrame;
+        Insets insets = Insets.of(0, 0, 0, 0);
+        for (int i = types.size() - 1; i >= 0; i--) {
+            final InsetsSource source = state.peekSource(types.valueAt(i));
+            if (source == null) {
+                continue;
+            }
+            insets = Insets.max(insets, source.calculateInsets(
+                    df, attrs.isFitInsetsIgnoringVisibility()));
+        }
+        final int left = (sidesToFit & WindowInsets.Side.LEFT) != 0 ? insets.left : 0;
+        final int top = (sidesToFit & WindowInsets.Side.TOP) != 0 ? insets.top : 0;
+        final int right = (sidesToFit & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0;
+        final int bottom = (sidesToFit & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0;
+        outBounds.set(df.left + left, df.top + top, df.right - right, df.bottom - bottom);
+    }
+
+    private Configuration getConfiguration() {
+        return mContext.getResources().getConfiguration();
+    }
+
     /**
      * We have one child
      */
@@ -1057,18 +1086,15 @@
                     controlInsetsForCompatibility(mWindowAttributes);
                     res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                             getHostVisibility(), mDisplay.getDisplayId(), userId,
-                            mInsetsController.getRequestedVisibility(), mTmpFrames.frame,
-                            inputChannel, mTempInsets, mTempControls);
+                            mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
+                            mTempControls);
                     if (mTranslator != null) {
-                        mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
                         mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
                     }
-                    setFrame(mTmpFrames.frame);
                 } catch (RemoteException e) {
                     mAdded = false;
                     mView = null;
                     mAttachInfo.mRootView = null;
-                    inputChannel = null;
                     mFallbackEventHandler.setView(null);
                     unscheduleTraversals();
                     setAccessibilityFocus(null, null);
@@ -1084,6 +1110,9 @@
                 mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;
                 mInsetsController.onStateChanged(mTempInsets);
                 mInsetsController.onControlsChanged(mTempControls);
+                computeWindowBounds(mWindowAttributes, mInsetsController.getState(),
+                        getConfiguration().windowConfiguration.getBounds(), mTmpFrames.frame);
+                setFrame(mTmpFrames.frame);
                 if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
                 if (res < WindowManagerGlobal.ADD_OKAY) {
                     mAttachInfo.mRootView = null;
@@ -1357,7 +1386,7 @@
     }
 
     private int getNightMode() {
-        return mContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+        return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
     }
 
     private void updateForceDarkMode() {
@@ -2333,7 +2362,7 @@
 
     /* package */ WindowInsets getWindowInsets(boolean forceConstruct) {
         if (mLastWindowInsets == null || forceConstruct) {
-            final Configuration config = mContext.getResources().getConfiguration();
+            final Configuration config = getConfiguration();
             mLastWindowInsets = mInsetsController.calculateInsets(
                     config.isScreenRound(), mAttachInfo.mAlwaysConsumeSystemBars,
                     mWindowAttributes.type, config.windowConfiguration.getWindowingMode(),
@@ -2469,7 +2498,7 @@
             mFullRedrawNeeded = true;
             mLayoutRequested = true;
 
-            final Configuration config = mContext.getResources().getConfiguration();
+            final Configuration config = getConfiguration();
             if (shouldUseDisplaySize(lp)) {
                 // NOTE -- system code, won't try to do compat mode.
                 Point size = new Point();
@@ -4762,7 +4791,7 @@
         }
         // TODO: Centralize this sanitization? Why do we let setting bad modes?
         // Alternatively, can we just let HWUI figure it out? Do we need to care here?
-        if (!mContext.getResources().getConfiguration().isScreenWideColorGamut()) {
+        if (!getConfiguration().isScreenWideColorGamut()) {
             colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
         }
         mAttachInfo.mThreadedRenderer.setColorMode(colorMode);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 63f1eed..9e87c95 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1511,9 +1511,7 @@
          *  Use {@link #dimAmount} to control the amount of dim. */
         public static final int FLAG_DIM_BEHIND        = 0x00000002;
 
-        /** Window flag: blur everything behind this window.
-         * @deprecated Blurring is no longer supported. */
-        @Deprecated
+        /** Window flag: enable blur behind for this window. */
         public static final int FLAG_BLUR_BEHIND        = 0x00000004;
 
         /** Window flag: this window won't ever get key input focus, so the
@@ -3233,11 +3231,16 @@
         public boolean preferMinimalPostProcessing = false;
 
         /**
-         * Indicates that this window wants to have blurred content behind it.
+         * Specifies the amount of blur to be used to blur everything behind the window.
+         * The effect is similar to the dimAmount, but instead of dimming, the content behind
+         * will be blurred.
          *
-         * @hide
+         * The blur behind radius range starts at 0, which means no blur, and increases until 150
+         * for the densest blur.
+         *
+         * @see #FLAG_BLUR_BEHIND
          */
-        public int backgroundBlurRadius = 0;
+        public int blurBehindRadius = 0;
 
         /**
          * The color mode requested by this window. The target display may
@@ -3626,7 +3629,7 @@
             out.writeInt(mFitInsetsSides);
             out.writeBoolean(mFitInsetsIgnoringVisibility);
             out.writeBoolean(preferMinimalPostProcessing);
-            out.writeInt(backgroundBlurRadius);
+            out.writeInt(blurBehindRadius);
             if (providesInsetsTypes != null) {
                 out.writeInt(providesInsetsTypes.length);
                 out.writeIntArray(providesInsetsTypes);
@@ -3695,7 +3698,7 @@
             mFitInsetsSides = in.readInt();
             mFitInsetsIgnoringVisibility = in.readBoolean();
             preferMinimalPostProcessing = in.readBoolean();
-            backgroundBlurRadius = in.readInt();
+            blurBehindRadius = in.readInt();
             int insetsTypesLength = in.readInt();
             if (insetsTypesLength > 0) {
                 providesInsetsTypes = new int[insetsTypesLength];
@@ -3940,8 +3943,8 @@
                 changes |= MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED;
             }
 
-            if (backgroundBlurRadius != o.backgroundBlurRadius) {
-                backgroundBlurRadius = o.backgroundBlurRadius;
+            if (blurBehindRadius != o.blurBehindRadius) {
+                blurBehindRadius = o.blurBehindRadius;
                 changes |= BACKGROUND_BLUR_RADIUS_CHANGED;
             }
 
@@ -4108,9 +4111,9 @@
                 sb.append(" preferMinimalPostProcessing=");
                 sb.append(preferMinimalPostProcessing);
             }
-            if (backgroundBlurRadius != 0) {
-                sb.append(" backgroundBlurRadius=");
-                sb.append(backgroundBlurRadius);
+            if (blurBehindRadius != 0) {
+                sb.append(" blurBehindRadius=");
+                sb.append(blurBehindRadius);
             }
             sb.append(System.lineSeparator());
             sb.append(prefix).append("  fl=").append(
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index dd0ab65..170124e 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -116,14 +116,6 @@
      */
     public static final int RELAYOUT_INSETS_PENDING = 0x1;
 
-    /**
-     * Flag for relayout: the client may be currently using the current surface,
-     * so if it is to be destroyed as a part of the relayout the destroy must
-     * be deferred until later.  The client will call performDeferredDestroy()
-     * when it is okay.
-     */
-    public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
-
     public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1;
     public static final int ADD_FLAG_APP_VISIBLE = 0x2;
     public static final int ADD_FLAG_USE_TRIPLE_BUFFERING = 0x4;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 5ae66e3..b85f1079 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -135,7 +135,7 @@
      */
     @Override
     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
+            int viewVisibility, int displayId, InsetsState requestedVisibility,
             InputChannel outInputChannel, InsetsState outInsetsState,
             InsetsSourceControl[] outActiveControls) {
         final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
@@ -171,10 +171,10 @@
     @Override
     public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
-            Rect outFrame, InputChannel outInputChannel, InsetsState outInsetsState,
+            InputChannel outInputChannel, InsetsState outInsetsState,
             InsetsSourceControl[] outActiveControls) {
         return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibility,
-                outFrame, outInputChannel, outInsetsState, outActiveControls);
+                outInputChannel, outInsetsState, outActiveControls);
     }
 
     @Override
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 73962d7..10fd0e0 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -82,6 +82,7 @@
     public static InlineSuggestionInfo newInlineSuggestionInfo(
             @NonNull InlinePresentationSpec presentationSpec,
             @NonNull @Source String source,
+            @SuppressLint("NullableCollection")
             @Nullable String[] autofillHints, @NonNull @Type String type, boolean isPinned) {
         return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
     }
diff --git a/core/java/android/view/inputmethod/OWNERS b/core/java/android/view/inputmethod/OWNERS
index e6a04da..d7db7c7 100644
--- a/core/java/android/view/inputmethod/OWNERS
+++ b/core/java/android/view/inputmethod/OWNERS
@@ -2,3 +2,5 @@
 set noparent
 
 include /services/core/java/com/android/server/inputmethod/OWNERS
+
+per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index fa46146..b49d3c0 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -101,15 +101,17 @@
                 }
                 break;
             case STATE_UI_TRANSLATION_PAUSED:
-                runForEachView((view) -> view.onPauseUiTranslation(), STATE_UI_TRANSLATION_PAUSED);
+                runForEachView(View::onPauseUiTranslation);
                 break;
             case STATE_UI_TRANSLATION_RESUMED:
-                runForEachView((view) -> view.onRestoreUiTranslation(),
-                        STATE_UI_TRANSLATION_PAUSED);
+                runForEachView(View::onRestoreUiTranslation);
                 break;
             case STATE_UI_TRANSLATION_FINISHED:
                 destroyTranslators();
-                runForEachView((view) -> view.onFinishUiTranslation(), STATE_UI_TRANSLATION_PAUSED);
+                runForEachView(View::onFinishUiTranslation);
+                synchronized (mLock) {
+                    mViews.clear();
+                }
                 break;
             default:
                 Log.w(TAG, "onAutoTranslationStateChange(): unknown state: " + state);
@@ -191,9 +193,6 @@
      */
     private void onUiTranslationStarted(Translator translator, List<AutofillId> views) {
         synchronized (mLock) {
-            if (views == null || views.size() == 0) {
-                throw new IllegalArgumentException("Invalid empty views: " + views);
-            }
             // Find Views collect the translation data
             // TODO(b/178084101): try to optimize, e.g. to this in a single traversal
             final int viewCounts = views.size();
@@ -223,22 +222,18 @@
         }
     }
 
-    private void runForEachView(Consumer<View> action, @UiTranslationState int state) {
+    private void runForEachView(Consumer<View> action) {
         synchronized (mLock) {
+            final ArrayMap<AutofillId, WeakReference<View>> views = new ArrayMap<>(mViews);
             mActivity.runOnUiThread(() -> {
-                final int viewCounts = mViews.size();
+                final int viewCounts = views.size();
                 for (int i = 0; i < viewCounts; i++) {
-                    final View view = mViews.valueAt(i).get();
+                    final View view = views.valueAt(i).get();
                     if (view == null) {
-                        Log.w(TAG, "The View for autofill id " + mViews.keyAt(i)
-                                + " may be gone for state " + stateToString(state));
                         continue;
                     }
                     action.accept(view);
                 }
-                if (state == STATE_UI_TRANSLATION_FINISHED) {
-                    mViews.clear();
-                }
             });
         }
     }
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index 046f75f..2452e4c 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.widget.RemoteViews.RemoteView;
 
 /**
  * <p>
@@ -52,6 +53,7 @@
  * {@link android.R.styleable#View View Attributes}
  * </p>
  */
+@RemoteView
 public class CheckBox extends CompoundButton {
     public CheckBox(Context context) {
         this(context, null);
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 135ff9f..63f8ee7 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -27,11 +27,13 @@
 import android.graphics.Canvas;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
+import android.view.RemotableViewMethod;
 import android.view.SoundEffectConstants;
 import android.view.ViewDebug;
 import android.view.ViewHierarchyEncoder;
@@ -275,6 +277,7 @@
      * @param resId the resource identifier of the drawable
      * @attr ref android.R.styleable#CompoundButton_button
      */
+    @RemotableViewMethod(asyncImpl = "setButtonDrawableAsync")
     public void setButtonDrawable(@DrawableRes int resId) {
         final Drawable d;
         if (resId != 0) {
@@ -285,6 +288,12 @@
         setButtonDrawable(d);
     }
 
+    /** @hide **/
+    public Runnable setButtonDrawableAsync(@DrawableRes int resId) {
+        Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+        return () -> setButtonDrawable(drawable);
+    }
+
     /**
      * Sets a drawable as the compound button image.
      *
@@ -336,6 +345,23 @@
     }
 
     /**
+     * Sets the button of this CompoundButton to the specified Icon.
+     *
+     * @param icon an Icon holding the desired button, or {@code null} to clear
+     *             the button
+     */
+    @RemotableViewMethod(asyncImpl = "setButtonIconAsync")
+    public void setButtonIcon(@Nullable Icon icon) {
+        setButtonDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+    }
+
+    /** @hide **/
+    public Runnable setButtonIconAsync(@Nullable Icon icon) {
+        Drawable button = icon == null ? null : icon.loadDrawable(getContext());
+        return () -> setButtonDrawable(button);
+    }
+
+    /**
      * Applies a tint to the button drawable. Does not modify the current tint
      * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
      * <p>
@@ -350,6 +376,7 @@
      * @see #setButtonTintList(ColorStateList)
      * @see Drawable#setTintList(ColorStateList)
      */
+    @RemotableViewMethod
     public void setButtonTintList(@Nullable ColorStateList tint) {
         mButtonTintList = tint;
         mHasButtonTint = true;
@@ -394,6 +421,7 @@
      * @see #getButtonTintMode()
      * @see Drawable#setTintBlendMode(BlendMode)
      */
+    @RemotableViewMethod
     public void setButtonTintBlendMode(@Nullable BlendMode tintMode) {
         mButtonBlendMode = tintMode;
         mHasButtonBlendMode = true;
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index a04d7c3..9b35034 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.RemoteViews.RemoteView;
 
 import com.android.internal.R;
 
@@ -49,6 +50,7 @@
  * {@link android.R.styleable#View View Attributes}
  * </p>
  */
+@RemoteView
 public class RadioButton extends CompoundButton {
 
     public RadioButton(Context context) {
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 4722fdc..d445fdc 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -31,6 +31,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews.RemoteView;
 
 import com.android.internal.R;
 
@@ -59,6 +60,7 @@
  * @see RadioButton
  *
  */
+@RemoteView
 public class RadioGroup extends LinearLayout {
     private static final String LOG_TAG = RadioGroup.class.getSimpleName();
 
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index b47a0ac..dfef7ca 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -132,6 +132,13 @@
  *   <li>{@link android.widget.TextClock}</li>
  *   <li>{@link android.widget.TextView}</li>
  * </ul>
+ * <p>As of API 31, the following widgets and layouts may also be used:</p>
+ * <ul>
+ *     <li>{@link android.widget.CheckBox}</li>
+ *     <li>{@link android.widget.RadioButton}</li>
+ *     <li>{@link android.widget.RadioGroup}</li>
+ *     <li>{@link android.widget.Switch}</li>
+ * </ul>
  * <p>Descendants of these classes are not supported.</p>
  */
 public class RemoteViews implements Parcelable, Filter {
@@ -185,6 +192,8 @@
     private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23;
     private static final int RESOURCE_REFLECTION_ACTION_TAG = 24;
     private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25;
+    private static final int SET_COMPOUND_BUTTON_CHECKED_TAG = 26;
+    private static final int SET_RADIO_GROUP_CHECKED = 27;
 
     /** @hide **/
     @IntDef(prefix = "MARGIN_", value = {
@@ -2552,6 +2561,87 @@
         }
     }
 
+    private static class SetCompoundButtonCheckedAction extends Action {
+
+        private final boolean mChecked;
+
+        SetCompoundButtonCheckedAction(@IdRes int viewId, boolean checked) {
+            this.viewId = viewId;
+            mChecked = checked;
+        }
+
+        SetCompoundButtonCheckedAction(Parcel in) {
+            viewId = in.readInt();
+            mChecked = in.readBoolean();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(viewId);
+            dest.writeBoolean(mChecked);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+                throws ActionException {
+            final View target = root.findViewById(viewId);
+            if (target == null) return;
+
+            if (!(target instanceof CompoundButton)) {
+                Log.w(LOG_TAG, "Cannot set checked to view "
+                        + viewId + " because it is not a CompoundButton");
+                return;
+            }
+
+            ((CompoundButton) target).setChecked(mChecked);
+        }
+
+        @Override
+        public int getActionTag() {
+            return SET_COMPOUND_BUTTON_CHECKED_TAG;
+        }
+    }
+
+    private static class SetRadioGroupCheckedAction extends Action {
+
+        @IdRes private final int mCheckedId;
+
+        SetRadioGroupCheckedAction(@IdRes int viewId, @IdRes int checkedId) {
+            this.viewId = viewId;
+            mCheckedId = checkedId;
+        }
+
+        SetRadioGroupCheckedAction(Parcel in) {
+            viewId = in.readInt();
+            mCheckedId = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(viewId);
+            dest.writeInt(mCheckedId);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+                throws ActionException {
+            final View target = root.findViewById(viewId);
+            if (target == null) return;
+
+            if (!(target instanceof RadioGroup)) {
+                Log.w(LOG_TAG, "Cannot check " + viewId + " because it's not a RadioGroup");
+                return;
+            }
+
+            ((RadioGroup) target).check(mCheckedId);
+        }
+
+        @Override
+        public int getActionTag() {
+            return SET_RADIO_GROUP_CHECKED;
+        }
+    }
+
     /**
      * Create a new RemoteViews object that will display the views contained
      * in the specified layout file.
@@ -2766,6 +2856,10 @@
                 return new ResourceReflectionAction(parcel);
             case COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG:
                 return new ComplexUnitDimensionReflectionAction(parcel);
+            case SET_COMPOUND_BUTTON_CHECKED_TAG:
+                return new SetCompoundButtonCheckedAction(parcel);
+            case SET_RADIO_GROUP_CHECKED:
+                return new SetRadioGroupCheckedAction(parcel);
             default:
                 throw new ActionException("Tag " + tag + " not found");
         }
@@ -3846,6 +3940,26 @@
     }
 
     /**
+     * Equivalent to calling {@link android.widget.CompoundButton#setChecked(boolean)}.
+     *
+     * @param viewId The id of the view whose property to set.
+     * @param checked true to check the button, false to uncheck it.
+     */
+    public void setCompoundButtonChecked(@IdRes int viewId, boolean checked) {
+        addAction(new SetCompoundButtonCheckedAction(viewId, checked));
+    }
+
+    /**
+     * Equivalent to calling {@link android.widget.RadioGroup#check(int)}.
+     *
+     * @param viewId The id of the view whose property to set.
+     * @param checkedId The unique id of the radio button to select in the group.
+     */
+    public void setRadioGroupChecked(@IdRes int viewId, @IdRes int checkedId) {
+        addAction(new SetRadioGroupCheckedAction(viewId, checkedId));
+    }
+
+    /**
      * Provides an alternate layout ID, which can be used to inflate this view. This layout will be
      * used by the host when the widgets displayed on a light-background where foreground elements
      * and text can safely draw using a dark color without any additional background protection.
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 3295fd2..d3600ef 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -35,6 +35,7 @@
 import android.graphics.Region.Op;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.text.Layout;
@@ -48,12 +49,14 @@
 import android.util.MathUtils;
 import android.view.Gravity;
 import android.view.MotionEvent;
+import android.view.RemotableViewMethod;
 import android.view.SoundEffectConstants;
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 import android.view.ViewStructure;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.inspector.InspectableProperty;
+import android.widget.RemoteViews.RemoteView;
 
 import com.android.internal.R;
 
@@ -84,6 +87,7 @@
  * @attr ref android.R.styleable#Switch_thumbTextPadding
  * @attr ref android.R.styleable#Switch_track
  */
+@RemoteView
 public class Switch extends CompoundButton {
     private static final int THUMB_ANIMATION_DURATION = 250;
 
@@ -441,6 +445,7 @@
      *
      * @attr ref android.R.styleable#Switch_switchPadding
      */
+    @RemotableViewMethod
     public void setSwitchPadding(int pixels) {
         mSwitchPadding = pixels;
         requestLayout();
@@ -466,6 +471,7 @@
      *
      * @attr ref android.R.styleable#Switch_switchMinWidth
      */
+    @RemotableViewMethod
     public void setSwitchMinWidth(int pixels) {
         mSwitchMinWidth = pixels;
         requestLayout();
@@ -491,6 +497,7 @@
      *
      * @attr ref android.R.styleable#Switch_thumbTextPadding
      */
+    @RemotableViewMethod
     public void setThumbTextPadding(int pixels) {
         mThumbTextPadding = pixels;
         requestLayout();
@@ -533,10 +540,17 @@
      *
      * @attr ref android.R.styleable#Switch_track
      */
+    @RemotableViewMethod(asyncImpl = "setTrackResourceAsync")
     public void setTrackResource(@DrawableRes int resId) {
         setTrackDrawable(getContext().getDrawable(resId));
     }
 
+    /** @hide **/
+    public Runnable setTrackResourceAsync(@DrawableRes int resId) {
+        Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+        return () -> setTrackDrawable(drawable);
+    }
+
     /**
      * Get the drawable used for the track that the switch slides within.
      *
@@ -550,6 +564,23 @@
     }
 
     /**
+     * Set the drawable used for the track that the switch slides within to the specified Icon.
+     *
+     * @param icon an Icon holding the desired track, or {@code null} to clear
+     *             the track
+     */
+    @RemotableViewMethod(asyncImpl = "setTrackIconAsync")
+    public void setTrackIcon(@Nullable Icon icon) {
+        setTrackDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+    }
+
+    /** @hide **/
+    public Runnable setTrackIconAsync(@Nullable Icon icon) {
+        Drawable track = icon == null ? null : icon.loadDrawable(getContext());
+        return () -> setTrackDrawable(track);
+    }
+
+    /**
      * Applies a tint to the track drawable. Does not modify the current
      * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
      * <p>
@@ -563,6 +594,7 @@
      * @see #getTrackTintList()
      * @see Drawable#setTintList(ColorStateList)
      */
+    @RemotableViewMethod
     public void setTrackTintList(@Nullable ColorStateList tint) {
         mTrackTintList = tint;
         mHasTrackTint = true;
@@ -607,6 +639,7 @@
      * @see #getTrackTintMode()
      * @see Drawable#setTintBlendMode(BlendMode)
      */
+    @RemotableViewMethod
     public void setTrackTintBlendMode(@Nullable BlendMode blendMode) {
         mTrackBlendMode = blendMode;
         mHasTrackTintMode = true;
@@ -686,10 +719,17 @@
      *
      * @attr ref android.R.styleable#Switch_thumb
      */
+    @RemotableViewMethod(asyncImpl = "setThumbResourceAsync")
     public void setThumbResource(@DrawableRes int resId) {
         setThumbDrawable(getContext().getDrawable(resId));
     }
 
+    /** @hide **/
+    public Runnable setThumbResourceAsync(@DrawableRes int resId) {
+        Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+        return () -> setThumbDrawable(drawable);
+    }
+
     /**
      * Get the drawable used for the switch "thumb" - the piece that the user
      * can physically touch and drag along the track.
@@ -704,6 +744,24 @@
     }
 
     /**
+     * Set the drawable used for the switch "thumb" - the piece that the user
+     * can physically touch and drag along the track - to the specified Icon.
+     *
+     * @param icon an Icon holding the desired thumb, or {@code null} to clear
+     *             the thumb
+     */
+    @RemotableViewMethod(asyncImpl = "setThumbIconAsync")
+    public void setThumbIcon(@Nullable Icon icon) {
+        setThumbDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+    }
+
+    /** @hide **/
+    public Runnable setThumbIconAsync(@Nullable Icon icon) {
+        Drawable track = icon == null ? null : icon.loadDrawable(getContext());
+        return () -> setThumbDrawable(track);
+    }
+
+    /**
      * Applies a tint to the thumb drawable. Does not modify the current
      * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
      * <p>
@@ -717,6 +775,7 @@
      * @see #getThumbTintList()
      * @see Drawable#setTintList(ColorStateList)
      */
+    @RemotableViewMethod
     public void setThumbTintList(@Nullable ColorStateList tint) {
         mThumbTintList = tint;
         mHasThumbTint = true;
@@ -761,6 +820,7 @@
      * @see #getThumbTintMode()
      * @see Drawable#setTintBlendMode(BlendMode)
      */
+    @RemotableViewMethod
     public void setThumbTintBlendMode(@Nullable BlendMode blendMode) {
         mThumbBlendMode = blendMode;
         mHasThumbTintMode = true;
@@ -822,6 +882,7 @@
      *
      * @attr ref android.R.styleable#Switch_splitTrack
      */
+    @RemotableViewMethod
     public void setSplitTrack(boolean splitTrack) {
         mSplitTrack = splitTrack;
         invalidate();
@@ -852,6 +913,7 @@
      *
      * @attr ref android.R.styleable#Switch_textOn
      */
+    @RemotableViewMethod
     public void setTextOn(CharSequence textOn) {
         mTextOn = textOn;
         requestLayout();
@@ -875,6 +937,7 @@
      *
      * @attr ref android.R.styleable#Switch_textOff
      */
+    @RemotableViewMethod
     public void setTextOff(CharSequence textOff) {
         mTextOff = textOff;
         requestLayout();
@@ -889,6 +952,7 @@
      * @param showText {@code true} to display on/off text
      * @attr ref android.R.styleable#Switch_showText
      */
+    @RemotableViewMethod
     public void setShowText(boolean showText) {
         if (mShowText != showText) {
             mShowText = showText;
diff --git a/core/java/com/android/internal/annotations/CompositeRWLock.java b/core/java/com/android/internal/annotations/CompositeRWLock.java
new file mode 100644
index 0000000..b6ddfc4
--- /dev/null
+++ b/core/java/com/android/internal/annotations/CompositeRWLock.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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.internal.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies a list of locks which are required for read/write operations on a data field.
+ *
+ * <p>
+ * To annotate methods accessing the data field with the annotation {@link CompositeRWLock},
+ * use {@link GuardedBy#value} to annotate method w/ write and/or read access to the data field,
+ * use {@link GuardedBy#anyOf} to annotate method w/ read only access to the data field.
+ * </p>
+ *
+ * <p>
+ * When its {@link #value()} consists of multiple locks:
+ * <ul>
+ *   <li>To write to the protected data, acquire <b>all</b> of the locks
+ *       in the order of the appearance in the {@link #value}.</li>
+ *   <li>To read from the protected data, acquire any of the locks in the {@link #value}.</li>
+ * </ul>
+ * </p>
+ */
+@Target({FIELD})
+@Retention(RetentionPolicy.CLASS)
+public @interface CompositeRWLock {
+    String[] value() default {};
+}
diff --git a/core/java/com/android/internal/annotations/GuardedBy.java b/core/java/com/android/internal/annotations/GuardedBy.java
index 0e63214..c05c4ab 100644
--- a/core/java/com/android/internal/annotations/GuardedBy.java
+++ b/core/java/com/android/internal/annotations/GuardedBy.java
@@ -16,7 +16,9 @@
 
 package com.android.internal.annotations;
 
-import java.lang.annotation.ElementType;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
@@ -25,8 +27,32 @@
  * Annotation type used to mark a method or field that can only be accessed when
  * holding the referenced locks.
  */
-@Target({ ElementType.FIELD, ElementType.METHOD })
+@Target({FIELD, METHOD})
 @Retention(RetentionPolicy.CLASS)
 public @interface GuardedBy {
-    String[] value();
+    /**
+     * Specifies a list of locks to be held in order to access the field/method
+     * annotated with this; when used in conjunction with the {@link CompositeRWLock}, locks
+     * should be acquired in the order of the appearance in the {@link #value} here.
+     *
+     * <p>
+     * If specified, {@link #anyOf()} must be null.
+     * </p>
+     *
+     * @see CompositeRWLock
+     */
+    String[] value() default {};
+
+    /**
+     * Specifies a list of locks where at least one of them must be held in order to access
+     * the field/method annotated with this; it should be <em>only</em> used in the conjunction
+     * with the {@link CompositeRWLock}.
+     *
+     * <p>
+     * If specified, {@link #allOf()} must be null.
+     * </p>
+     *
+     * @see CompositeRWLock
+     */
+    String[] anyOf() default {};
 }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 55f8c40..c1952c7 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -52,7 +52,7 @@
 
     // Remaining methods are only used in Java.
 
-    BatteryUsageStats getBatteryUsageStats(in BatteryUsageStatsQuery query);
+    List<BatteryUsageStats> getBatteryUsageStats(in List<BatteryUsageStatsQuery> queries);
 
     @UnsupportedAppUsage
     byte[] getStatistics();
diff --git a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
index cafe0de..dfcc914 100644
--- a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
+++ b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
@@ -17,8 +17,8 @@
 package com.android.internal.graphics.fonts;
 
 import android.os.ParcelFileDescriptor;
+import android.graphics.fonts.FontUpdateRequest;
 import android.text.FontConfig;
-import android.graphics.fonts.SystemFontState;
 
 /**
  * System private interface for talking with
@@ -28,5 +28,5 @@
 interface IFontManager {
     FontConfig getFontConfig();
 
-    int updateFont(in ParcelFileDescriptor fd, in byte[] signature, int baseVersion);
+    int updateFont(int baseVersion, in FontUpdateRequest request);
 }
diff --git a/core/java/com/android/internal/listeners/ListenerTransport.java b/core/java/com/android/internal/listeners/ListenerTransport.java
index 9d6210e..1a6870c 100644
--- a/core/java/com/android/internal/listeners/ListenerTransport.java
+++ b/core/java/com/android/internal/listeners/ListenerTransport.java
@@ -16,54 +16,43 @@
 
 package com.android.internal.listeners;
 
-
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
- * A listener registration object which holds data associated with a listener, such the executor
- * the listener should run on.
+ * A listener transport object which can run listener operations on an executor.
  *
  * @param <TListener> listener type
  */
-public class ListenerTransport<TListener> {
-
-    private final Executor mExecutor;
-
-    private volatile @Nullable TListener mListener;
-
-    protected ListenerTransport(@NonNull Executor executor, @NonNull TListener listener) {
-        Preconditions.checkArgument(executor != null, "invalid null executor");
-        Preconditions.checkArgument(listener != null, "invalid null listener/callback");
-        mExecutor = executor;
-        mListener = listener;
-    }
+public interface ListenerTransport<TListener> {
 
     /**
-     * Prevents any listener invocations that happen-after this call.
+     * Should return a valid listener until {@link #unregister()} is invoked, and must return
+     * null after that. Recommended (but not required) that this is implemented via a volatile
+     * variable.
      */
-    public final void unregister() {
-        mListener = null;
-    }
+    @Nullable TListener getListener();
+
+    /**
+     * Must be implemented so that {@link #getListener()} returns null after this is invoked.
+     */
+    void unregister();
 
     /**
      * Executes the given operation for the listener.
      */
-    public final void execute(@NonNull Consumer<TListener> operation) {
+    default void execute(Executor executor, Consumer<TListener> operation) {
         Objects.requireNonNull(operation);
 
-        if (mListener == null) {
+        if (getListener() == null) {
             return;
         }
 
-        mExecutor.execute(() -> {
-            TListener listener = mListener;
+        executor.execute(() -> {
+            TListener listener = getListener();
             if (listener == null) {
                 return;
             }
@@ -71,15 +60,4 @@
             operation.accept(listener);
         });
     }
-
-    @Override
-    public final boolean equals(Object obj) {
-        // intentionally bound to reference equality so removal works as expected
-        return this == obj;
-    }
-
-    @Override
-    public final int hashCode() {
-        return super.hashCode();
-    }
 }
diff --git a/core/java/com/android/internal/listeners/ListenerTransportManager.java b/core/java/com/android/internal/listeners/ListenerTransportManager.java
new file mode 100644
index 0000000..0d5d1b7b
--- /dev/null
+++ b/core/java/com/android/internal/listeners/ListenerTransportManager.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 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.internal.listeners;
+
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * A listener transport manager which handles mappings between the client facing listener and system
+ * server facing transport. Supports transports which may be removed either from the client side or
+ * from the system server side without leaking memory.
+ *
+ * @param <TTransport>> transport type
+ */
+public abstract class ListenerTransportManager<TTransport extends ListenerTransport<?>> {
+
+    @GuardedBy("mRegistrations")
+    private final Map<Object, WeakReference<TTransport>> mRegistrations;
+
+    protected ListenerTransportManager() {
+        // using weakhashmap means that the transport may be GCed if the server drops its reference,
+        // and thus the listener may be GCed as well if the client drops that reference. if the
+        // server will never drop a reference without warning (ie, transport removal may only be
+        // initiated from the client side), then arraymap or similar may be used without fear of
+        // memory leaks.
+        mRegistrations = new WeakHashMap<>();
+    }
+
+    /**
+     * Adds a new transport with the given listener key.
+     */
+    public final void addListener(Object key, TTransport transport) {
+        try {
+            synchronized (mRegistrations) {
+                // ordering of operations is important so that if an error occurs at any point we
+                // are left in a reasonable state
+                registerTransport(transport);
+                WeakReference<TTransport> oldTransportRef = mRegistrations.put(key,
+                        new WeakReference<>(transport));
+                if (oldTransportRef != null) {
+                    TTransport oldTransport = oldTransportRef.get();
+                    if (oldTransport != null) {
+                        oldTransport.unregister();
+                        unregisterTransport(oldTransport);
+                    }
+                }
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes the transport with the given listener key.
+     */
+    public final void removeListener(Object key) {
+        try {
+            synchronized (mRegistrations) {
+                // ordering of operations is important so that if an error occurs at any point we
+                // are left in a reasonable state
+                WeakReference<TTransport> transportRef = mRegistrations.remove(key);
+                if (transportRef != null) {
+                    TTransport transport = transportRef.get();
+                    if (transport != null) {
+                        transport.unregister();
+                        unregisterTransport(transport);
+                    }
+                }
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    protected abstract void registerTransport(TTransport transport) throws RemoteException;
+
+    protected abstract void unregisterTransport(TTransport transport) throws RemoteException;
+}
diff --git a/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java b/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java
deleted file mode 100644
index fc1d69f..0000000
--- a/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2020 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.internal.listeners;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Build;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-import android.util.IndentingPrintWriter;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import java.io.FileDescriptor;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-/**
- * A listener multiplexer designed for use by client-side code. This class ensures that listeners
- * are never invoked while a lock is held. This class is only useful for multiplexing listeners -
- * if all client listeners can be combined into a single server request, and all server results will
- * be delivered to all clients.
- *
- * By default, the multiplexer will replace requests on the server simply by registering the new
- * request and trusting the server to know this is replacing the old request. If the server needs to
- * have the old request unregistered first, subclasses should override
- * {@link #reregisterWithServer(Object, Object)}.
- *
- * @param <TRequest>  listener request type, may be Void
- * @param <TListener> listener type
- */
-public abstract class ListenerTransportMultiplexer<TRequest, TListener> {
-
-    private final Object mLock = new Object();
-
-    @GuardedBy("mLock")
-    private ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> mRegistrations =
-            new ArrayMap<>();
-
-    @GuardedBy("mLock")
-    private boolean mServiceRegistered = false;
-
-    @GuardedBy("mLock")
-    private TRequest mCurrentRequest;
-
-    /**
-     * Should be implemented to register the given merged request with the server.
-     *
-     * @see #reregisterWithServer(Object, Object)
-     */
-    protected abstract void registerWithServer(TRequest mergedRequest) throws RemoteException;
-
-    /**
-     * Invoked when the server already has a request registered, and it is being replaced with a new
-     * request. The default implementation simply registers the new request, trusting the server to
-     * overwrite the old request.
-     */
-    protected void reregisterWithServer(TRequest oldMergedRequest, TRequest mergedRequest)
-            throws RemoteException {
-        registerWithServer(mergedRequest);
-    }
-
-    /**
-     * Should be implemented to unregister from the server.
-     */
-    protected abstract void unregisterWithServer() throws RemoteException;
-
-    /**
-     * Called in order to generate a merged request from the given requests. The list of requests
-     * will never be empty.
-     */
-    protected @Nullable TRequest mergeRequests(Collection<TRequest> requests) {
-        if (Build.IS_DEBUGGABLE) {
-            for (TRequest request : requests) {
-                // if using non-null requests then implementations must override this method
-                Preconditions.checkState(request == null);
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Adds a new listener with no request, using the listener as the key.
-     */
-    public void addListener(@NonNull TListener listener, @NonNull Executor executor) {
-        addListener(listener, null, listener, executor);
-    }
-
-    /**
-     * Adds a new listener with the given request, using the listener as the key.
-     */
-    public void addListener(@Nullable TRequest request, @NonNull TListener listener,
-            @NonNull Executor executor) {
-        addListener(listener, request, listener, executor);
-    }
-
-    /**
-     * Adds a new listener with the given request using a custom key.
-     */
-    public void addListener(@NonNull Object key, @Nullable TRequest request,
-            @NonNull TListener listener, @NonNull Executor executor) {
-        Objects.requireNonNull(key);
-        RequestListenerTransport<TRequest, TListener> registration =
-                new RequestListenerTransport<>(request, executor, listener);
-
-        synchronized (mLock) {
-            ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> newRegistrations =
-                    new ArrayMap<>(mRegistrations.size() + 1);
-            newRegistrations.putAll(mRegistrations);
-            RequestListenerTransport<TRequest, TListener> old = newRegistrations.put(key,
-                    registration);
-            mRegistrations = newRegistrations;
-
-            if (old != null) {
-                old.unregister();
-            }
-
-            updateService();
-        }
-    }
-
-    /**
-     * Removes the listener with the given key.
-     */
-    public void removeListener(@NonNull Object key) {
-        Objects.requireNonNull(key);
-
-        synchronized (mLock) {
-            if (!mRegistrations.containsKey(key)) {
-                return;
-            }
-
-            ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> newRegistrations =
-                    new ArrayMap<>(mRegistrations);
-            RequestListenerTransport<TRequest, TListener> old = newRegistrations.remove(key);
-            mRegistrations = newRegistrations;
-
-            if (old != null) {
-                old.unregister();
-                updateService();
-            }
-        }
-    }
-
-    private void updateService() {
-        synchronized (mLock) {
-            if (mRegistrations.isEmpty()) {
-                mCurrentRequest = null;
-                if (mServiceRegistered) {
-                    try {
-                        mServiceRegistered = false;
-                        unregisterWithServer();
-                    } catch (RemoteException e) {
-                        throw e.rethrowFromSystemServer();
-                    }
-                }
-                return;
-            }
-
-            ArrayList<TRequest> requests = new ArrayList<>(mRegistrations.size());
-            for (int i = 0; i < mRegistrations.size(); i++) {
-                requests.add(mRegistrations.valueAt(i).getRequest());
-            }
-
-            TRequest merged = mergeRequests(requests);
-            if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
-                TRequest old = mCurrentRequest;
-                mCurrentRequest = null;
-                try {
-                    if (mServiceRegistered) {
-                        // if a remote exception is thrown the service should not be registered
-                        mServiceRegistered = false;
-                        reregisterWithServer(old, merged);
-                    } else {
-                        registerWithServer(merged);
-                    }
-                    mCurrentRequest = merged;
-                    mServiceRegistered = true;
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            }
-        }
-    }
-
-    protected final void deliverToListeners(Consumer<TListener> operation) {
-        ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> registrations;
-        synchronized (mLock) {
-            registrations = mRegistrations;
-        }
-
-        try {
-            for (int i = 0; i < registrations.size(); i++) {
-                registrations.valueAt(i).execute(operation);
-            }
-        } finally {
-            onOperationFinished(operation);
-        }
-    }
-
-    /**
-     * Invoked when an operation is finished. This method will always be called once for every call
-     * to {@link #deliverToListeners(Consumer)}, regardless of whether the operation encountered any
-     * error or failed to execute in any way for any listeners.
-     */
-    protected void onOperationFinished(@NonNull Consumer<TListener> operation) {}
-
-    /**
-     * Dumps debug information.
-     */
-    public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
-        ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> registrations;
-        synchronized (mLock) {
-            registrations = mRegistrations;
-
-            ipw.print("service: ");
-            if (mServiceRegistered) {
-                if (mCurrentRequest == null) {
-                    ipw.print("request registered");
-                } else {
-                    ipw.print("request registered - " + mCurrentRequest);
-                }
-            } else {
-                ipw.print("unregistered");
-            }
-            ipw.println();
-        }
-
-        if (!registrations.isEmpty()) {
-            ipw.println("listeners:");
-
-            ipw.increaseIndent();
-            for (int i = 0; i < registrations.size(); i++) {
-                ipw.print(registrations.valueAt(i));
-            }
-            ipw.decreaseIndent();
-        }
-    }
-}
diff --git a/core/java/com/android/internal/listeners/RequestListenerTransport.java b/core/java/com/android/internal/listeners/RequestListenerTransport.java
deleted file mode 100644
index 178de06..0000000
--- a/core/java/com/android/internal/listeners/RequestListenerTransport.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2020 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.internal.listeners;
-
-import android.annotation.Nullable;
-
-import java.util.concurrent.Executor;
-
-/**
- * A listener transport with an associated request.
- *
- * @param <TRequest>  request type
- * @param <TListener> listener type
- */
-public class RequestListenerTransport<TRequest, TListener> extends ListenerTransport<TListener> {
-
-    private final @Nullable TRequest mRequest;
-
-    protected RequestListenerTransport(@Nullable TRequest request, Executor executor,
-            TListener listener) {
-        super(executor, listener);
-        mRequest = request;
-    }
-
-    /**
-     * Returns the request associated with this transport.
-     */
-    public final @Nullable TRequest getRequest() {
-        return mRequest;
-    }
-}
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index 6609ebe..8fe17fb 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -54,6 +54,7 @@
                     .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah)
                     .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs);
         }
+        // TODO(b/178140704): Attribute *measured* total usage for BatteryUsageStats.
     }
 
     /**
@@ -66,7 +67,8 @@
     public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
         final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType);
-        final double powerMah = mPowerEstimator.calculatePower(durationMs);
+        final double powerMah = getMeasuredOrEstimatedPower(
+                batteryStats.getScreenDozeEnergy(), durationMs);
         if (powerMah > 0) {
             BatterySipper bs = new BatterySipper(BatterySipper.DrainType.AMBIENT_DISPLAY, null, 0);
             bs.usagePowerMah = powerMah;
@@ -79,4 +81,12 @@
     private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) {
         return batteryStats.getScreenDozeTime(rawRealtimeUs, statsType) / 1000;
     }
+
+    private double getMeasuredOrEstimatedPower(long measuredEnergyUJ, long durationMs) {
+        if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) {
+            return mAhToUJ(measuredEnergyUJ);
+        } else {
+            return mPowerEstimator.calculatePower(durationMs);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index c8805dd..af61f91 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -35,6 +35,8 @@
     /**
      * Smeared power from screen usage.
      * We split the screen usage power and smear them among apps, based on activity time.
+     * The actual screen usage power may be measured or estimated, affecting the granularity and
+     * accuracy of the smearing, but the smearing algorithm is essentially the same.
      */
     public double screenPowerMah;
 
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index fcf8bb4..aa5015a 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -37,7 +37,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.telephony.TelephonyManager;
-import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2b034b0..1f7a7aa 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -21,8 +21,6 @@
 import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
 import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
 
-import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -107,7 +105,7 @@
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
 import com.android.internal.power.MeasuredEnergyStats;
-import com.android.internal.power.MeasuredEnergyStats.EnergyBucket;
+import com.android.internal.power.MeasuredEnergyStats.StandardEnergyBucket;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FrameworkStatsLog;
@@ -173,7 +171,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    static final int VERSION = 191 + (USE_OLD_HISTORY ? 1000 : 0);
+    static final int VERSION = 193 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -362,7 +360,6 @@
 
     public interface PlatformIdleStateCallback {
         public void fillLowPowerStats(RpmStats rpmStats);
-        public String getPlatformLowPowerStats();
         public String getSubsystemLowPowerStats();
     }
 
@@ -3482,11 +3479,6 @@
         }
         if (computeStepDetails) {
             if (mPlatformIdleStateCallback != null) {
-                mCurHistoryStepDetails.statPlatformIdleState =
-                        mPlatformIdleStateCallback.getPlatformLowPowerStats();
-                if (DEBUG) Slog.i(TAG, "WRITE PlatformIdleState:" +
-                        mCurHistoryStepDetails.statPlatformIdleState);
-
                 mCurHistoryStepDetails.statSubsystemPowerState =
                         mPlatformIdleStateCallback.getSubsystemLowPowerStats();
                 if (DEBUG) Slog.i(TAG, "WRITE SubsystemPowerState:" +
@@ -7167,8 +7159,8 @@
         if (mGlobalMeasuredEnergyStats == null) {
             return ENERGY_DATA_UNAVAILABLE;
         }
-        return mGlobalMeasuredEnergyStats.getAccumulatedBucketEnergy(
-                MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
+        return mGlobalMeasuredEnergyStats
+                .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
     }
 
     @Override
@@ -7176,8 +7168,8 @@
         if (mGlobalMeasuredEnergyStats == null) {
             return ENERGY_DATA_UNAVAILABLE;
         }
-        return mGlobalMeasuredEnergyStats.getAccumulatedBucketEnergy(
-                MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
+        return mGlobalMeasuredEnergyStats
+                .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
     }
 
     @Override public long getStartClockTime() {
@@ -7941,27 +7933,27 @@
             return mUidMeasuredEnergyStats;
         }
 
-        /** Adds the given energy to the given energy bucket for this uid. */
-        private void addEnergyToEnergyBucketLocked(long energyDeltaUJ,
-                @MeasuredEnergyStats.EnergyBucket int energyBucket, boolean accumulate) {
+        /** Adds the given energy to the given standard energy bucket for this uid. */
+        private void addEnergyToStandardBucketLocked(long energyDeltaUJ,
+                @StandardEnergyBucket int energyBucket, boolean accumulate) {
             getOrCreateMeasuredEnergyStatsLocked()
-                    .updateBucket(energyBucket, energyDeltaUJ, accumulate);
+                    .updateStandardBucket(energyBucket, energyDeltaUJ, accumulate);
         }
 
         /**
-         * Returns the energy used by this uid for an energy bucket of interest.
-         * @param bucket energy bucket of interest
+         * Returns the energy used by this uid for a standard energy bucket of interest.
+         * @param bucket standard energy bucket of interest
          * @return energy (in microjoules) used by this uid for this energy bucket
          */
-        public long getMeasuredEnergyMicroJoules(@MeasuredEnergyStats.EnergyBucket int bucket) {
+        public long getMeasuredEnergyMicroJoules(@StandardEnergyBucket int bucket) {
             if (mBsi.mGlobalMeasuredEnergyStats == null
-                    || !mBsi.mGlobalMeasuredEnergyStats.isEnergyBucketSupported(bucket)) {
+                    || !mBsi.mGlobalMeasuredEnergyStats.isStandardBucketSupported(bucket)) {
                 return ENERGY_DATA_UNAVAILABLE;
             }
             if (mUidMeasuredEnergyStats == null) {
                 return 0L; // It is supported, but was never filled, so it must be 0
             }
-            return mUidMeasuredEnergyStats.getAccumulatedBucketEnergy(bucket);
+            return mUidMeasuredEnergyStats.getAccumulatedStandardBucketEnergy(bucket);
         }
 
         /**
@@ -8633,11 +8625,7 @@
 
         @Override
         public long getScreenOnEnergy() {
-            if (mUidMeasuredEnergyStats == null) {
-                return ENERGY_DATA_UNAVAILABLE;
-            }
-            return mUidMeasuredEnergyStats.getAccumulatedBucketEnergy(
-                    MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
+            return getMeasuredEnergyMicroJoules(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
         }
 
         void initNetworkActivityLocked() {
@@ -12406,7 +12394,7 @@
             return;
         }
 
-        final @EnergyBucket int energyBucket =
+        final @StandardEnergyBucket int energyBucket =
                 MeasuredEnergyStats.getDisplayEnergyBucket(mScreenStateAtLastEnergyMeasurement);
         mScreenStateAtLastEnergyMeasurement = screenState;
 
@@ -12425,7 +12413,7 @@
             return;
         }
 
-        mGlobalMeasuredEnergyStats.updateBucket(energyBucket, energyUJ, true);
+        mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ, true);
 
         // Now we blame individual apps, but only if the display was ON.
         if (energyBucket != MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON) {
@@ -12463,7 +12451,7 @@
             final long appDisplayEnergyMJ =
                     (totalDisplayEnergyMJ * fgTimeMs + (totalFgTimeMs / 2))
                     / totalFgTimeMs;
-            uid.addEnergyToEnergyBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true);
+            uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true);
 
             // To mitigate round-off errors, remove this app from numerator & denominator totals
             totalDisplayEnergyMJ -= appDisplayEnergyMJ;
@@ -14148,33 +14136,34 @@
     /**
      * Initialize the measured energy stats data structures.
      *
-     * @param supportedEnergyBuckets boolean array indicating which buckets are currently supported
+     * @param supportedStandardBuckets boolean array indicating which {@link StandardEnergyBucket}s
+     *                                 are currently supported.
+     *                                 If null, none are supported (regardless of numCustomBuckets).
+     * @param numCustomBuckets number of custom (OTHER) EnergyConsumers on this device
      */
     @GuardedBy("this")
-    public void initMeasuredEnergyStatsLocked(boolean[] supportedEnergyBuckets) {
+    public void initMeasuredEnergyStatsLocked(@Nullable boolean[] supportedStandardBuckets,
+            int numCustomBuckets) {
         boolean supportedBucketMismatch = false;
         mScreenStateAtLastEnergyMeasurement = mScreenState;
 
-        if (supportedEnergyBuckets == null) {
+        if (supportedStandardBuckets == null) {
             if (mGlobalMeasuredEnergyStats != null) {
                 // Measured energy buckets no longer supported, wipe out the existing data.
                 supportedBucketMismatch = true;
             }
         } else if (mGlobalMeasuredEnergyStats == null) {
-            mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets);
+            mGlobalMeasuredEnergyStats
+                    = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
             return;
         } else {
-            for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
-                if (mGlobalMeasuredEnergyStats.isEnergyBucketSupported(i)
-                        != supportedEnergyBuckets[i]) {
-                    mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets);
-                    supportedBucketMismatch = true;
-                    break;
-                }
-            }
+            supportedBucketMismatch = !mGlobalMeasuredEnergyStats.isSupportEqualTo(
+                    supportedStandardBuckets, numCustomBuckets);
         }
 
         if (supportedBucketMismatch) {
+            mGlobalMeasuredEnergyStats = supportedStandardBuckets == null ?
+                    null : new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
             // Supported energy buckets changed since last boot.
             // Existing data is no longer reliable.
             resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime());
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 964568c..e76e34f 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -83,7 +83,7 @@
     /**
      * Returns a snapshot of battery attribution data.
      */
-    public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
+    public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
 
         // TODO(b/174186345): instead of BatteryStatsHelper, use PowerCalculators directly.
         final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(mContext,
@@ -100,17 +100,21 @@
 
         batteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, users);
 
+        ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size());
+        for (int i = 0; i < queries.size(); i++) {
+            results.add(getBatteryUsageStats(queries.get(i), batteryStatsHelper, users));
+        }
+        return results;
+    }
+
+    private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
+            BatteryStatsHelper batteryStatsHelper, SparseArray<UserHandle> users) {
         // TODO(b/174186358): read extra power component number from configuration
         final int customPowerComponentCount = 0;
         final int customTimeComponentCount = 0;
-        final boolean includeModeledComponents =
-                (query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED)
-                        != 0;
-
 
         final BatteryUsageStats.Builder batteryUsageStatsBuilder =
-                new BatteryUsageStats.Builder(customPowerComponentCount, customTimeComponentCount,
-                        includeModeledComponents)
+                new BatteryUsageStats.Builder(customPowerComponentCount, customTimeComponentCount)
                         .setDischargePercentage(batteryStatsHelper.getStats().getDischargeAmount(0))
                         .setConsumedPower(batteryStatsHelper.getTotalPower());
 
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 7972924..11c8761 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -122,11 +122,5 @@
                 .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, cpuTimeMs)
                 .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, cpuFgTimeMs)
                 .setPackageWithHighestDrain(packageWithHighestDrain);
-
-        if ((query.getFlags()
-                & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED) != 0) {
-            app.setConsumedPowerForCustomComponent(BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
-                    + BatteryConsumer.POWER_COMPONENT_CPU, cpuPowerMah);
-        }
     }
 }
diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java
index 974894f..05fcc70 100644
--- a/core/java/com/android/internal/os/PowerCalculator.java
+++ b/core/java/com/android/internal/os/PowerCalculator.java
@@ -102,7 +102,7 @@
 
         // TODO(b/175156498): Temporary code during the transition from BatterySippers to
         //  BatteryConsumers.
-        UidBatteryConsumer.Builder builder = new UidBatteryConsumer.Builder(0, 0, false, u);
+        UidBatteryConsumer.Builder builder = new UidBatteryConsumer.Builder(0, 0, u);
         calculateApp(builder, u, rawRealtimeUs, rawUptimeUs, BatteryUsageStatsQuery.DEFAULT);
         final UidBatteryConsumer uidBatteryConsumer = builder.build();
         app.cpuPowerMah = uidBatteryConsumer.getConsumedPower(
@@ -162,4 +162,10 @@
         // Use English locale because this is never used in UI (only in checkin and dump).
         return String.format(Locale.ENGLISH, format, power);
     }
+
+    static double mAhToUJ(long energyUJ) {
+        // TODO(b/173765509): Convert properly. This is mJ / V * (h/3600s) = mAh with V = 3.7 fixed.
+        //                    Leaving for later since desired units of energy have yet to be decided
+        return energyUJ / 1000.0 / 3.7  / 3600;
+    }
 }
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index 9c4a267..c86c795 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -24,7 +24,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.format.DateUtils;
-import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseLongArray;
 
@@ -62,7 +62,8 @@
                     .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs)
                     .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah);
         }
-        // TODO(b/178140704): Attribute screen usage similar to smearScreenBatterySipper.
+        // TODO(b/178140704): Attribute *measured* total usage for BatteryUsageStats.
+        // TODO(b/178140704): Attribute (measured/smeared) usage *per app* for BatteryUsageStats.
     }
 
     /**
@@ -71,19 +72,45 @@
     @Override
     public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
-        final long durationMs = computeDuration(batteryStats, rawRealtimeUs, statsType);
-        final double powerMah = computePower(batteryStats, rawRealtimeUs, statsType, durationMs);
-        if (powerMah != 0) {
-            final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0);
-            bs.usagePowerMah = powerMah;
-            bs.usageTimeMs = durationMs;
-            bs.sumPower();
-            sippers.add(bs);
 
+        final long energyUJ = batteryStats.getScreenOnEnergy();
+        final boolean isMeasuredDataAvailable = energyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE;
+
+        final long durationMs = computeDuration(batteryStats, rawRealtimeUs, statsType);
+        final double powerMah = getMeasuredOrComputedPower(
+                energyUJ, batteryStats, rawRealtimeUs, statsType, durationMs);
+        if (powerMah == 0) {
+            return;
+        }
+
+        // First deal with the SCREEN BatterySipper (since we need this for smearing over apps).
+        final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0);
+        bs.usagePowerMah = powerMah;
+        bs.usageTimeMs = durationMs;
+        bs.sumPower();
+        sippers.add(bs);
+
+        // Now deal with each app's BatterySipper. The results are stored in the screenPowerMah
+        // field, which is considered smeared, but the method depends on the data source.
+        if (isMeasuredDataAvailable) {
+            super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
+        } else {
             smearScreenBatterySipper(sippers, bs);
         }
     }
 
+    @Override
+    protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+            long rawUptimeUs, int statsType) {
+        final long energyUJ = u.getScreenOnEnergy();
+        if (energyUJ < 0) {
+            Slog.wtf(TAG, "Screen energy not supported, so calculateApp shouldn't de called");
+            return;
+        }
+        if (energyUJ == 0) return;
+        app.screenPowerMah = mAhToUJ(u.getScreenOnEnergy());
+    }
+
     private long computeDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) {
         return batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000;
     }
@@ -97,7 +124,7 @@
             final double binPowerMah = mScreenFullPowerEstimator.calculatePower(brightnessTime)
                     * (i + 0.5f) / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
             if (DEBUG && binPowerMah != 0) {
-                Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
+                Slog.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
                         + " power=" + formatCharge(binPowerMah));
             }
             power += binPowerMah;
@@ -105,6 +132,16 @@
         return power;
     }
 
+    private double getMeasuredOrComputedPower(long measuredEnergyUJ,
+            BatteryStats batteryStats, long rawRealtimeUs, int statsType, long durationMs) {
+
+        if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) {
+            return mAhToUJ(measuredEnergyUJ);
+        } else {
+            return computePower(batteryStats, rawRealtimeUs, statsType, durationMs);
+        }
+    }
+
     /**
      * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
      * time, and store this in the {@link BatterySipper#screenPowerMah} field.
@@ -124,10 +161,11 @@
         }
 
         if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
-            final double screenPowerMah = screenSipper.totalPowerMah;
+            final double totalScreenPowerMah = screenSipper.totalPowerMah;
             for (int i = sippers.size() - 1; i >= 0; i--) {
                 final BatterySipper sipper = sippers.get(i);
-                sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
+                sipper.screenPowerMah = totalScreenPowerMah
+                        * activityTimeArray.get(sipper.getUid(), 0)
                         / totalActivityTimeMs;
             }
         }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 4b343af..d196d4a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -117,6 +117,8 @@
     public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17;
 
     public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
+
+    public static final int MEMORY_TAG_LEVEL_NONE = 0;
     /**
      * Enable pointer tagging in this process.
      * Tags are checked during memory deallocation, but not on access.
@@ -160,7 +162,12 @@
      * GWP-ASan is activated unconditionally (but still, only a small subset of
      * allocations is protected).
      */
-    public static final int GWP_ASAN_LEVEL_ALWAYS = 2 << 21;
+    public static final int GWP_ASAN_LEVEL_ALWAYS = 1 << 22;
+
+    /**
+     * Enable automatic zero-initialization of native heap memory allocations.
+     */
+    public static final int NATIVE_HEAP_ZERO_INIT = 1 << 23;
 
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 141dc79..5df175e 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2540,8 +2540,15 @@
             }
         }
 
-        params.backgroundBlurRadius = a.getDimensionPixelSize(
-                R.styleable.Window_windowBackgroundBlurRadius, 0);
+        if (a.getBoolean(R.styleable.Window_windowBlurBehindEnabled, false)) {
+            if ((getForcedWindowFlags() & WindowManager.LayoutParams.FLAG_BLUR_BEHIND) == 0) {
+                params.flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
+            }
+
+            params.blurBehindRadius = a.getDimensionPixelSize(
+                    android.R.styleable.Window_windowBlurBehindRadius, 0);
+        }
+
 
         if (params.windowAnimations == 0) {
             params.windowAnimations = a.getResourceId(
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index b744a5d..e310f8d 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -20,8 +20,10 @@
 import static android.os.BatteryStats.ENERGY_DATA_UNAVAILABLE;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
+import android.util.DebugUtils;
 import android.util.Slog;
 import android.view.Display;
 
@@ -33,7 +35,9 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Tracks the measured energy usage of various subsystems according to their {@link EnergyBucket}.
+ * Tracks the measured energy usage of various subsystems according to their
+ * {@link StandardEnergyBucket} or custom energy bucket (which is tied to
+ * {@link android.hardware.power.stats.EnergyConsumer.ordinal}).
  *
  * This class doesn't use a TimeBase, and instead requires manually decisions about when to
  * accumulate since it is trivial. However, in the future, a TimeBase could be used instead.
@@ -42,15 +46,13 @@
 public class MeasuredEnergyStats {
     private static final String TAG = "MeasuredEnergyStats";
 
-    // Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if energy
-    // bucket integers are modified.
+    // Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} MUST be updated if standard
+    // energy bucket integers are modified/added/removed.
     public static final int ENERGY_BUCKET_UNKNOWN = -1;
     public static final int ENERGY_BUCKET_SCREEN_ON = 0;
     public static final int ENERGY_BUCKET_SCREEN_DOZE = 1;
     public static final int ENERGY_BUCKET_SCREEN_OTHER = 2;
-    public static final int NUMBER_ENERGY_BUCKETS = 3;
-    private static final String[] ENERGY_BUCKET_NAMES =
-            {"screen-on", "screen-doze", "screen-other"};
+    public static final int NUMBER_STANDARD_ENERGY_BUCKETS = 3; // Buckets above this are custom.
 
     @IntDef(prefix = {"ENERGY_BUCKET_"}, value = {
             ENERGY_BUCKET_UNKNOWN,
@@ -59,28 +61,37 @@
             ENERGY_BUCKET_SCREEN_OTHER,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface EnergyBucket {
+    public @interface StandardEnergyBucket {
     }
 
     /**
-     * Total energy (in microjoules) that an {@link EnergyBucket} has accumulated since the last
-     * reset. Values MUST be non-zero or ENERGY_DATA_UNAVAILABLE. Accumulation only occurs
+     * Total energy (in microjoules) that an energy bucket (including both
+     * {@link StandardEnergyBucket} and custom buckets) has accumulated since the last reset.
+     * Values MUST be non-zero or ENERGY_DATA_UNAVAILABLE. Accumulation only occurs
      * while the necessary conditions are satisfied (e.g. on battery).
      *
+     * Energy for both {@link StandardEnergyBucket}s and custom energy buckets are stored in this
+     * array, and may internally both referred to as 'buckets'. This is an implementation detail;
+     * externally, we differentiate between these two data sources.
+     *
      * Warning: Long array is used for access speed. If the number of supported subsystems
      * becomes large, consider using an alternate data structure such as a SparseLongArray.
      */
-    private final long[] mAccumulatedEnergiesMicroJoules = new long[NUMBER_ENERGY_BUCKETS];
+    private final long[] mAccumulatedEnergiesMicroJoules;
 
     /**
      * Creates a MeasuredEnergyStats set to support the provided energy buckets.
-     * supportedEnergyBuckets should generally be of size {@link #NUMBER_ENERGY_BUCKETS}.
+     * supportedStandardBuckets must be of size {@link #NUMBER_STANDARD_ENERGY_BUCKETS}.
+     * numCustomBuckets >= 0 is the number of (non-standard) custom energy buckets on the device.
      */
-    public MeasuredEnergyStats(boolean[] supportedEnergyBuckets) {
+    public MeasuredEnergyStats(boolean[] supportedStandardBuckets, int numCustomBuckets) {
+        final int numTotalBuckets = NUMBER_STANDARD_ENERGY_BUCKETS + numCustomBuckets;
+        mAccumulatedEnergiesMicroJoules = new long[numTotalBuckets];
         // Initialize to all zeros where supported, otherwise ENERGY_DATA_UNAVAILABLE.
-        for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
-            if (!supportedEnergyBuckets[bucket]) {
-                mAccumulatedEnergiesMicroJoules[bucket] = ENERGY_DATA_UNAVAILABLE;
+        // All custom buckets are, by definition, supported, so their values stay at 0.
+        for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) {
+            if (!supportedStandardBuckets[stdBucket]) {
+                mAccumulatedEnergiesMicroJoules[stdBucket] = ENERGY_DATA_UNAVAILABLE;
             }
         }
     }
@@ -90,10 +101,13 @@
      * supported. This certainly does NOT produce an exact clone of the template.
      */
     private MeasuredEnergyStats(MeasuredEnergyStats template) {
+        final int numIndices = template.getNumberOfIndices();
+        mAccumulatedEnergiesMicroJoules = new long[numIndices];
         // Initialize to all zeros where supported, otherwise ENERGY_DATA_UNAVAILABLE.
-        for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
-            if (!template.isEnergyBucketSupported(bucket)) {
-                mAccumulatedEnergiesMicroJoules[bucket] = ENERGY_DATA_UNAVAILABLE;
+        // All custom buckets are, by definition, supported, so their values stay at 0.
+        for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) {
+            if (!template.isIndexSupported(stdBucket)) {
+                mAccumulatedEnergiesMicroJoules[stdBucket] = ENERGY_DATA_UNAVAILABLE;
             }
         }
     }
@@ -108,18 +122,22 @@
 
     /**
      * Constructor for creating a temp MeasuredEnergyStats.
-     * See {@link #readSummaryFromParcel(MeasuredEnergyStats, Parcel)}.
+     * See {@link #createAndReadSummaryFromParcel(Parcel, MeasuredEnergyStats)}.
      */
-    private MeasuredEnergyStats() {
+    private MeasuredEnergyStats(int numIndices) {
+        mAccumulatedEnergiesMicroJoules = new long[numIndices];
     }
 
     /** Construct from parcel. */
     public MeasuredEnergyStats(Parcel in) {
+        final int size = in.readInt();
+        mAccumulatedEnergiesMicroJoules = new long[size];
         in.readLongArray(mAccumulatedEnergiesMicroJoules);
     }
 
     /** Write to parcel */
     public void writeToParcel(Parcel out) {
+        out.writeInt(mAccumulatedEnergiesMicroJoules.length);
         out.writeLongArray(mAccumulatedEnergiesMicroJoules);
     }
 
@@ -129,16 +147,18 @@
      * summary parcel was written. Availability has already been correctly set in the constructor.
      * Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary
      *       parceling changes.
+     *
+     * Corresponding write performed by {@link #writeSummaryToParcel(Parcel, boolean)}.
      */
     private void readSummaryFromParcel(Parcel in, boolean overwriteAvailability) {
-        final int size = in.readInt();
-        for (int i = 0; i < size; i++) {
-            final int bucket = in.readInt();
+        final int numWrittenEntries = in.readInt();
+        for (int entry = 0; entry < numWrittenEntries; entry++) {
+            final int index = in.readInt();
             final long energyUJ = in.readLong();
             if (overwriteAvailability) {
-                mAccumulatedEnergiesMicroJoules[bucket] = energyUJ;
+                mAccumulatedEnergiesMicroJoules[index] = energyUJ;
             } else {
-                setValueIfSupported(bucket, energyUJ);
+                setValueIfSupported(index, energyUJ);
             }
         }
     }
@@ -146,52 +166,90 @@
     /**
      * Write to summary parcel.
      * Note: Measured subsystem availability may be different when the summary parcel is read.
+     *
+     * Corresponding read performed by {@link #readSummaryFromParcel(Parcel, boolean)}.
      */
     private void writeSummaryToParcel(Parcel out, boolean skipZero) {
-        final int sizePos = out.dataPosition();
+        final int posOfNumWrittenEntries = out.dataPosition();
         out.writeInt(0);
-        int size = 0;
-        // Write only the supported buckets with non-zero energy.
-        for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
-            final long energy = mAccumulatedEnergiesMicroJoules[i];
+        int numWrittenEntries = 0;
+        // Write only the supported buckets (with non-zero energy, if applicable).
+        for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
+            final long energy = mAccumulatedEnergiesMicroJoules[index];
             if (energy < 0) continue;
             if (energy == 0 && skipZero) continue;
 
-            out.writeInt(i);
-            out.writeLong(mAccumulatedEnergiesMicroJoules[i]);
-            size++;
+            out.writeInt(index);
+            out.writeLong(mAccumulatedEnergiesMicroJoules[index]);
+            numWrittenEntries++;
         }
         final int currPos = out.dataPosition();
-        out.setDataPosition(sizePos);
-        out.writeInt(size);
+        out.setDataPosition(posOfNumWrittenEntries);
+        out.writeInt(numWrittenEntries);
         out.setDataPosition(currPos);
     }
 
-    /** Updates the given bucket with the given energy iff accumulate is true. */
-    public void updateBucket(@EnergyBucket int bucket, long energyDeltaUJ, boolean accumulate) {
+    /** Get number of possible buckets, including both standard and custom ones. */
+    private int getNumberOfIndices() {
+        return mAccumulatedEnergiesMicroJoules.length;
+    }
+
+    // TODO: Get rid of the 'accumulate' boolean. It's always true.
+    /** Updates the given standard energy bucket with the given energy if accumulate is true. */
+    public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ,
+            boolean accumulate) {
+        checkValidStandardBucket(bucket);
+        updateEntry(bucket, energyDeltaUJ, accumulate);
+    }
+
+    /** Updates the given custom energy bucket with the given energy if accumulate is true. */
+    public void updateCustomBucket(int customBucket, long energyDeltaUJ, boolean accumulate) {
+        if (!isValidCustomBucket(customBucket)) {
+            Slog.e(TAG, "Attempted to update invalid custom bucket " + customBucket);
+            return;
+        }
+        final int index = customBucketToIndex(customBucket);
+        updateEntry(index, energyDeltaUJ, accumulate);
+    }
+
+    /** Updates the given index with the given energy if accumulate is true. */
+    private void updateEntry(int index, long energyDeltaUJ, boolean accumulate) {
         if (accumulate) {
-            if (mAccumulatedEnergiesMicroJoules[bucket] >= 0L) {
-                mAccumulatedEnergiesMicroJoules[bucket] += energyDeltaUJ;
+            if (mAccumulatedEnergiesMicroJoules[index] >= 0L) {
+                mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ;
             } else {
                 Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket "
-                        + ENERGY_BUCKET_NAMES[bucket] + " whose value was "
-                        + mAccumulatedEnergiesMicroJoules[bucket]);
+                        + getBucketName(index) + " whose value was "
+                        + mAccumulatedEnergiesMicroJoules[index]);
             }
         }
     }
 
     /**
-     * Return accumulated energy (in microjoules) for the given energy bucket since last reset.
-     * Returns {@link BatteryStats#ENERGY_DATA_UNAVAILABLE} if this energy data is unavailable.
+     * Return accumulated energy (in microjoules) for a standard energy bucket since last reset.
+     * Returns {@link android.os.BatteryStats#ENERGY_DATA_UNAVAILABLE} if this data is unavailable.
+     * @throws IllegalArgumentException if no such {@link StandardEnergyBucket}.
      */
-    public long getAccumulatedBucketEnergy(@EnergyBucket int bucket) {
+    public long getAccumulatedStandardBucketEnergy(@StandardEnergyBucket int bucket) {
+        checkValidStandardBucket(bucket);
         return mAccumulatedEnergiesMicroJoules[bucket];
     }
 
     /**
-     * Map {@link MeasuredEnergySubsystem} and device state to a Display {@link EnergyBucket}.
+     * Return accumulated energy (in microjoules) for the a custom energy bucket since last reset.
+     * Returns {@link android.os.BatteryStats#ENERGY_DATA_UNAVAILABLE} if this data is unavailable.
      */
-    public static @EnergyBucket int getDisplayEnergyBucket(int screenState) {
+    public long getAccumulatedCustomBucketEnergy(int customBucket) {
+        if (!isValidCustomBucket(customBucket)) {
+            return ENERGY_DATA_UNAVAILABLE;
+        }
+        return mAccumulatedEnergiesMicroJoules[customBucketToIndex(customBucket)];
+    }
+
+    /**
+     * Map {@link MeasuredEnergySubsystem} and device state to Display {@link StandardEnergyBucket}.
+     */
+    public static @StandardEnergyBucket int getDisplayEnergyBucket(int screenState) {
         if (Display.isOnState(screenState)) {
             return ENERGY_BUCKET_SCREEN_ON;
         }
@@ -204,15 +262,20 @@
     /**
      * Create a MeasuredEnergyStats object from a summary parcel.
      *
+     * Corresponding write performed by
+     * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel, boolean)}.
+     *
      * @return a new MeasuredEnergyStats object as described.
      *         Returns null if the parcel indicates there is no data to populate.
      */
     public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in) {
+        final int arraySize = in.readInt();
         // Check if any MeasuredEnergyStats exists on the parcel
-        if (in.readInt() == 0) return null;
+        if (arraySize == 0) return null;
 
-        final MeasuredEnergyStats stats =
-                new MeasuredEnergyStats(new boolean[NUMBER_ENERGY_BUCKETS]);
+        final int numCustomBuckets = arraySize - NUMBER_STANDARD_ENERGY_BUCKETS;
+        final MeasuredEnergyStats stats = new MeasuredEnergyStats(
+                new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], numCustomBuckets);
         stats.readSummaryFromParcel(in, true);
         return stats;
     }
@@ -221,6 +284,12 @@
      * Create a MeasuredEnergyStats using the template to determine which buckets are supported,
      * and populate this new object from the given parcel.
      *
+     * The parcel must be consistent with the template in terms of the number of
+     * possible (not necessarily supported) standard and custom buckets.
+     *
+     * Corresponding write performed by
+     * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel, boolean)}.
+     *
      * @return a new MeasuredEnergyStats object as described.
      *         Returns null if the stats contain no non-0 information (such as if template is null
      *         or if the parcel indicates there is no data to populate).
@@ -229,12 +298,22 @@
      */
     public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in,
             @Nullable MeasuredEnergyStats template) {
+        final int arraySize = in.readInt();
         // Check if any MeasuredEnergyStats exists on the parcel
-        if (in.readInt() == 0) return null;
+        if (arraySize == 0) return null;
 
         if (template == null) {
-            // Nothing supported now. Create placeholder object just to consume the parcel data.
-            final MeasuredEnergyStats mes = new MeasuredEnergyStats();
+            // Nothing supported anymore. Create placeholder object just to consume the parcel data.
+            final MeasuredEnergyStats mes = new MeasuredEnergyStats(arraySize);
+            mes.readSummaryFromParcel(in, false);
+            return null;
+        }
+
+        if (arraySize != template.getNumberOfIndices()) {
+            Slog.wtf(TAG, "Size of MeasuredEnergyStats parcel (" + arraySize
+                    + ") does not match template (" + template.getNumberOfIndices() + ").");
+            // Something is horribly wrong. Just consume the parcel and return null.
+            final MeasuredEnergyStats mes = new MeasuredEnergyStats(arraySize);
             mes.readSummaryFromParcel(in, false);
             return null;
         }
@@ -251,14 +330,17 @@
 
     /** Returns true iff any of the buckets are supported and non-zero. */
     private boolean containsInterestingData() {
-        for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
-            if (mAccumulatedEnergiesMicroJoules[bucket] > 0) return true;
+        for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
+            if (mAccumulatedEnergiesMicroJoules[index] > 0) return true;
         }
         return false;
     }
 
     /**
      * Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0.
+     *
+     * Corresponding read performed by {@link #createAndReadSummaryFromParcel(Parcel)}
+     * and {@link #createAndReadSummaryFromParcel(Parcel, MeasuredEnergyStats)}.
      */
     public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats,
             Parcel dest, boolean skipZero) {
@@ -266,14 +348,15 @@
             dest.writeInt(0);
             return;
         }
-        dest.writeInt(1);
+        dest.writeInt(stats.getNumberOfIndices());
         stats.writeSummaryToParcel(dest, skipZero);
     }
 
     /** Reset accumulated energy. */
     private void reset() {
-        for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
-            setValueIfSupported(bucket, 0L);
+        final int numIndices = getNumberOfIndices();
+        for (int index = 0; index < numIndices; index++) {
+            setValueIfSupported(index, 0L);
         }
     }
 
@@ -282,33 +365,93 @@
         if (stats != null) stats.reset();
     }
 
-    /** If the bucket is AVAILABLE, overwrite its value; otherwise leave it as UNAVAILABLE. */
-    private void setValueIfSupported(@EnergyBucket int bucket, long value) {
-        if (mAccumulatedEnergiesMicroJoules[bucket] != ENERGY_DATA_UNAVAILABLE) {
-            mAccumulatedEnergiesMicroJoules[bucket] = value;
+    /** If the index is AVAILABLE, overwrite its value; otherwise leave it as UNAVAILABLE. */
+    private void setValueIfSupported(int index, long value) {
+        if (mAccumulatedEnergiesMicroJoules[index] != ENERGY_DATA_UNAVAILABLE) {
+            mAccumulatedEnergiesMicroJoules[index] = value;
         }
     }
 
-    /** Check if measuring the energy of the given bucket is supported by this device. */
-    public boolean isEnergyBucketSupported(@EnergyBucket int bucket) {
-        return mAccumulatedEnergiesMicroJoules[bucket] != ENERGY_DATA_UNAVAILABLE;
+    /**
+     * Check if measuring the energy of the given bucket is supported by this device.
+     * @throws IllegalArgumentException if not a valid {@link StandardEnergyBucket}.
+     */
+    public boolean isStandardBucketSupported(@StandardEnergyBucket int bucket) {
+        checkValidStandardBucket(bucket);
+        return isIndexSupported(bucket);
+    }
+
+    private boolean isIndexSupported(int index) {
+        return mAccumulatedEnergiesMicroJoules[index] != ENERGY_DATA_UNAVAILABLE;
+    }
+
+    /** Check if the supported energy buckets are precisely those given. */
+    public boolean isSupportEqualTo(
+            @NonNull boolean[] queriedStandardBuckets, int numCustomBuckets) {
+
+        final int numBuckets = getNumberOfIndices();
+        // TODO(b/178504428): Detect whether custom buckets have changed qualitatively, not just
+        //                    quantitatively, and treat as mismatch if so.
+        if (numBuckets != NUMBER_STANDARD_ENERGY_BUCKETS + numCustomBuckets) {
+            return false;
+        }
+        for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) {
+            if (isStandardBucketSupported(stdBucket) != queriedStandardBuckets[stdBucket]) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /** Dump debug data. */
     public void dump(PrintWriter pw) {
         pw.println("Accumulated energy since last reset (microjoules):");
         pw.print("   ");
-        for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
-            pw.print(ENERGY_BUCKET_NAMES[bucket]);
+        for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
+            pw.print(getBucketName(index));
             pw.print(" : ");
-            pw.print(mAccumulatedEnergiesMicroJoules[bucket]);
-            if (!isEnergyBucketSupported(bucket)) {
+            pw.print(mAccumulatedEnergiesMicroJoules[index]);
+            if (!isIndexSupported(index)) {
                 pw.print(" (unsupported)");
             }
-            if (bucket != NUMBER_ENERGY_BUCKETS - 1) {
+            if (index != mAccumulatedEnergiesMicroJoules.length - 1) {
                 pw.print(", ");
             }
         }
         pw.println();
     }
+
+    /**
+     * If the index is a standard bucket, returns its name; otherwise returns its prefixed custom
+     * bucket number.
+     */
+    private static String getBucketName(int index) {
+        if (isValidStandardBucket(index)) {
+            return DebugUtils.valueToString(MeasuredEnergyStats.class, "ENERGY_BUCKET_", index);
+        }
+        return "CUSTOM_" + indexToCustomBucket(index);
+    }
+
+    private static int customBucketToIndex(int customBucket) {
+        return customBucket + NUMBER_STANDARD_ENERGY_BUCKETS;
+    }
+
+    private static int indexToCustomBucket(int index) {
+        return index - NUMBER_STANDARD_ENERGY_BUCKETS;
+    }
+
+    private static void checkValidStandardBucket(@StandardEnergyBucket int bucket) {
+        if (!isValidStandardBucket(bucket)) {
+            throw new IllegalArgumentException("Illegal StandardEnergyBucket " + bucket);
+        }
+    }
+
+    private static boolean isValidStandardBucket(@StandardEnergyBucket int bucket) {
+        return bucket >= 0 && bucket < NUMBER_STANDARD_ENERGY_BUCKETS;
+    }
+
+    private boolean isValidCustomBucket(int customBucket) {
+        return customBucket >= 0
+                && customBucketToIndex(customBucket) < mAccumulatedEnergiesMicroJoules.length;
+    }
 }
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 3cf00ae..c6fd6ee 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -35,6 +35,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.function.IntFunction;
 
 /**
@@ -599,6 +600,20 @@
         return cur;
     }
 
+    /**
+     * Similar to {@link Set#addAll(Collection)}}, but with support for set values of {@code null}.
+     */
+    public static @NonNull <T> ArraySet<T> addAll(@Nullable ArraySet<T> cur,
+            @Nullable Collection<T> val) {
+        if (cur == null) {
+            cur = new ArraySet<>();
+        }
+        if (val != null) {
+            cur.addAll(val);
+        }
+        return cur;
+    }
+
     public static @Nullable <T> ArraySet<T> remove(@Nullable ArraySet<T> cur, T val) {
         if (cur == null) {
             return null;
diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java
index dd64c40..1ab316d 100644
--- a/core/java/com/android/internal/util/Parcelling.java
+++ b/core/java/com/android/internal/util/Parcelling.java
@@ -27,6 +27,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 import java.util.regex.Pattern;
 
 /**
@@ -291,5 +292,19 @@
                 return s == null ? null : Pattern.compile(s);
             }
         }
+
+        class ForUUID implements Parcelling<UUID> {
+
+            @Override
+            public void parcel(UUID item, Parcel dest, int parcelFlags) {
+                dest.writeString(item == null ? null : item.toString());
+            }
+
+            @Override
+            public UUID unparcel(Parcel source) {
+                String string = source.readString();
+                return string == null ? null : UUID.fromString(string);
+            }
+        }
     }
 }
diff --git a/core/java/com/android/internal/view/ScrollCaptureInternal.java b/core/java/com/android/internal/view/ScrollCaptureInternal.java
index ae1a815..4b9a160 100644
--- a/core/java/com/android/internal/view/ScrollCaptureInternal.java
+++ b/core/java/com/android/internal/view/ScrollCaptureInternal.java
@@ -59,6 +59,11 @@
     public static final int TYPE_RECYCLING = 2;
 
     /**
+     * The ViewGroup scrolls, but has no child views in
+     */
+    private static final int TYPE_OPAQUE = 3;
+
+    /**
      * Performs tests on the given View and determines:
      * 1. If scrolling is possible
      * 2. What mechanisms are used for scrolling.
@@ -95,8 +100,15 @@
             }
             return TYPE_RECYCLING;
         }
+        // At least one child view is required.
+        if (((ViewGroup) view).getChildCount() < 1) {
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "scrollable with no children");
+            }
+            return TYPE_OPAQUE;
+        }
         if (DEBUG_VERBOSE) {
-            Log.v(TAG, "hint: less than two child views");
+            Log.v(TAG, "hint: single child view");
         }
         //Because recycling containers don't use scrollY, a non-zero value means Scroll view.
         if (view.getScrollY() != 0) {
diff --git a/core/java/com/android/internal/widget/DisableImageView.java b/core/java/com/android/internal/widget/DisableImageView.java
new file mode 100644
index 0000000..0d9bf71
--- /dev/null
+++ b/core/java/com/android/internal/widget/DisableImageView.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 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.internal.widget;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+/**
+ * ImageView which applies a saturation image filter, used by AppWidgets to represent disabled icon
+ */
+@RemoteViews.RemoteView
+public class DisableImageView extends ImageView {
+
+    public DisableImageView(Context context) {
+        this(context, null, 0, 0);
+    }
+
+    public DisableImageView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0, 0);
+    }
+
+    public DisableImageView(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public DisableImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        // Apply the disabled filter
+        ColorMatrix brightnessMatrix = new ColorMatrix();
+        float brightnessF = 0.5f;
+        int brightnessI = (int) (255 * brightnessF);
+        // Brightness: C-new = C-old*(1-amount) + amount
+        float scale = 1f - brightnessF;
+        float[] mat = brightnessMatrix.getArray();
+        mat[0] = scale;
+        mat[6] = scale;
+        mat[12] = scale;
+        mat[4] = brightnessI;
+        mat[9] = brightnessI;
+        mat[14] = brightnessI;
+
+        ColorMatrix filterMatrix = new ColorMatrix();
+        filterMatrix.setSaturation(0);
+        filterMatrix.preConcat(brightnessMatrix);
+        setColorFilter(new ColorMatrixColorFilter(filterMatrix));
+    }
+}
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index 3fc3933..d284d51 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -11,6 +11,7 @@
 per-file *Messaging* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file *Message* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file *Conversation* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *People* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file *ImageResolver* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file CallLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
 per-file CachingIconView.java = file:/services/core/java/com/android/server/notification/OWNERS
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index b4d8e50..95999a7 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -23,7 +23,6 @@
 import android.os.Build;
 import android.os.DropBoxManager;
 import android.os.Environment;
-import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.RecoverySystem;
 import android.os.RemoteException;
@@ -74,7 +73,6 @@
         SystemProperties.getInt("ro.debuggable", 0) == 1 ? 196608 : 65536;
     private static final int GMSCORE_LASTK_LOG_SIZE = 196608;
 
-    private static final File TOMBSTONE_DIR = new File("/data/tombstones");
     private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
 
     // The pre-froyo package and class of the system updater, which
@@ -85,9 +83,6 @@
     private static final String OLD_UPDATER_CLASS =
         "com.google.android.systemupdater.SystemUpdateReceiver";
 
-    // Keep a reference to the observer so the finalizer doesn't disable it.
-    private static FileObserver sTombstoneObserver = null;
-
     private static final String LOG_FILES_FILE = "log-files.xml";
     private static final AtomicFile sFile = new AtomicFile(new File(
             Environment.getDataSystemDirectory(), LOG_FILES_FILE), "log-files");
@@ -153,7 +148,7 @@
         Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
     }
 
-    private String getPreviousBootHeaders() {
+    private static String getPreviousBootHeaders() {
         try {
             return FileUtils.readTextFile(lastHeaderFile, 0, null);
         } catch (IOException e) {
@@ -161,7 +156,7 @@
         }
     }
 
-    private String getCurrentBootHeaders() throws IOException {
+    private static String getCurrentBootHeaders() throws IOException {
         return new StringBuilder(512)
             .append("Build: ").append(Build.FINGERPRINT).append("\n")
             .append("Hardware: ").append(Build.BOARD).append("\n")
@@ -175,7 +170,7 @@
     }
 
 
-    private String getBootHeadersToLogAndUpdate() throws IOException {
+    private static String getBootHeadersToLogAndUpdate() throws IOException {
         final String oldHeaders = getPreviousBootHeaders();
         final String newHeaders = getCurrentBootHeaders();
 
@@ -247,38 +242,27 @@
         logFsMountTime();
         addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK");
         logSystemServerShutdownTimeMetrics();
-
-        // Scan existing tombstones (in case any new ones appeared)
-        File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
-        for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
-            if (tombstoneFiles[i].isFile()) {
-                addFileToDropBox(db, timestamps, headers, tombstoneFiles[i].getPath(),
-                        LOG_SIZE, "SYSTEM_TOMBSTONE");
-            }
-        }
-
         writeTimestamps(timestamps);
+    }
 
-        // Start watching for new tombstone files; will record them as they occur.
-        // This gets registered with the singleton file observer thread.
-        sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CREATE) {
-            @Override
-            public void onEvent(int event, String path) {
-                HashMap<String, Long> timestamps = readTimestamps();
-                try {
-                    File file = new File(TOMBSTONE_DIR, path);
-                    if (file.isFile() && file.getName().startsWith("tombstone_")) {
-                        addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
-                                TAG_TOMBSTONE);
-                    }
-                } catch (IOException e) {
-                    Slog.e(TAG, "Can't log tombstone", e);
-                }
-                writeTimestamps(timestamps);
-            }
-        };
-
-        sTombstoneObserver.startWatching();
+    /**
+     * Add a tombstone to the DropBox.
+     *
+     * @param ctx Context
+     * @param tombstone path to the tombstone
+     */
+    public static void addTombstoneToDropBox(Context ctx, File tombstone) {
+        final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
+        final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
+        HashMap<String, Long> timestamps = readTimestamps();
+        try {
+            final String headers = getBootHeadersToLogAndUpdate();
+            addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
+                    TAG_TOMBSTONE);
+        } catch (IOException e) {
+            Slog.e(TAG, "Can't log tombstone", e);
+        }
+        writeTimestamps(timestamps);
     }
 
     private static void addLastkToDropBox(
@@ -761,7 +745,7 @@
         }
     }
 
-    private void writeTimestamps(HashMap<String, Long> timestamps) {
+    private static void writeTimestamps(HashMap<String, Long> timestamps) {
         synchronized (sFile) {
             final FileOutputStream stream;
             try {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index e5bc470..cb586d6 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -178,8 +178,9 @@
     // be delivered anonymously even to apps which target O+.
     final ArraySet<String> mAllowImplicitBroadcasts = new ArraySet<>();
 
-    // These are the package names of apps which should be in the 'always'
-    // URL-handling state upon factory reset.
+    // These are the package names of apps which should be automatically granted domain verification
+    // for all of their domains. The only way these apps can be overridden by the user is by
+    // explicitly disabling overall link handling support in app info.
     final ArraySet<String> mLinkedApps = new ArraySet<>();
 
     // These are the components that are enabled by default as VR mode listener services.
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 787d348..3acbd1e 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -36,6 +36,7 @@
 #include <utils/List.h>
 #include <utils/KeyedVector.h>
 #include <binder/Parcel.h>
+#include <binder/ParcelRef.h>
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
 #include <utils/threads.h>
@@ -515,8 +516,9 @@
 
 static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
 {
-    Parcel* parcel = new Parcel();
-    return reinterpret_cast<jlong>(parcel);
+    sp<ParcelRef> parcelRef = ParcelRef::create();
+    parcelRef->incStrong(reinterpret_cast<const void*>(android_os_Parcel_create));
+    return reinterpret_cast<jlong>(static_cast<Parcel *>(parcelRef.get()));
 }
 
 static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -529,8 +531,8 @@
 
 static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
 {
-    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
-    delete parcel;
+    ParcelRef* derivative = static_cast<ParcelRef*>(reinterpret_cast<Parcel*>(nativePtr));
+    derivative->decStrong(reinterpret_cast<const void*>(android_os_Parcel_create));
 }
 
 static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 675648a..f7b3f30 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -35,6 +35,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/Parcel.h>
+#include <binder/ParcelRef.h>
 #include <binder/ProcessState.h>
 #include <binder/Stability.h>
 #include <binderthreadstate/CallerUtils.h>
@@ -1367,7 +1368,8 @@
 }
 
 static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
-        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
+        jint code, jobject dataObj, jobject replyObj, jboolean replyObjOwnsNativeParcel,
+        jint flags) // throws RemoteException
 {
     if (dataObj == NULL) {
         jniThrowNullPointerException(env, NULL);
@@ -1409,6 +1411,21 @@
     status_t err = target->transact(code, *data, reply, flags);
     //if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
 
+    if (reply) {
+        if (replyObjOwnsNativeParcel) {
+            // as per Parcel java class constructor, here, "reply" MUST be a "ParcelRef"
+            // only for Parcel that contained Binder objects
+            if (reply->objectsCount() > 0) {
+                IPCThreadState::self()->createTransactionReference(static_cast<ParcelRef*>(reply));
+            }
+        } else {
+            // as per Parcel.java, if Parcel java object NOT owning native Parcel object, it will
+            // NOT destroy the native Parcel object upon GC(finalize()), so, there will be no race
+            // condtion in this case. Please refer to the java class methods: Parcel.finalize(),
+            // Parcel.destroy().
+        }
+    }
+
     if (kEnableBinderSample) {
         if (time_binder_calls) {
             conditionally_log_binder_call(start_millis, target, code);
@@ -1535,7 +1552,7 @@
     {"pingBinder",          "()Z", (void*)android_os_BinderProxy_pingBinder},
     {"isBinderAlive",       "()Z", (void*)android_os_BinderProxy_isBinderAlive},
     {"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
-    {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
+    {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;ZI)Z", (void*)android_os_BinderProxy_transact},
     {"linkToDeath",         "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
     {"unlinkToDeath",       "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
     {"getNativeFinalizer",  "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 05fcaec..8597308 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1409,16 +1409,6 @@
     transaction->deferTransactionUntil_legacy(ctrl, barrier, frameNumber);
 }
 
-static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong transactionObj,
-        jlong nativeObject,
-        jlong newParentObject) {
-
-    auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    auto newParent = reinterpret_cast<SurfaceControl *>(newParentObject);
-    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-    transaction->reparentChildren(ctrl, newParent);
-}
-
 static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject,
         jlong newParentObject) {
@@ -1838,8 +1828,6 @@
             (void*)nativeGetProtectedContentSupport },
     {"nativeDeferTransactionUntil", "(JJJJ)V",
             (void*)nativeDeferTransactionUntil },
-    {"nativeReparentChildren", "(JJJ)V",
-            (void*)nativeReparentChildren } ,
     {"nativeReparent", "(JJJ)V",
             (void*)nativeReparent },
     {"nativeCaptureDisplay",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index c5a9559..3d1c38d 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -339,6 +339,7 @@
     GWP_ASAN_LEVEL_NEVER = 0 << 21,
     GWP_ASAN_LEVEL_LOTTERY = 1 << 21,
     GWP_ASAN_LEVEL_ALWAYS = 2 << 21,
+    NATIVE_HEAP_ZERO_INIT = 1 << 23,
 };
 
 enum UnsolicitedZygoteMessageTypes : uint32_t {
@@ -1778,15 +1779,20 @@
   }
   mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level);
 
+  // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
+  runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
+
   // Avoid heap zero initialization for applications without MTE. Zero init may
   // cause app compat problems, use more memory, or reduce performance. While it
   // would be nice to have them for apps, we will have to wait until they are
   // proven out, have more efficient hardware, and/or apply them only to new
   // applications.
-  mallopt(M_BIONIC_ZERO_INIT, 0);
+  if (!(runtime_flags & RuntimeFlags::NATIVE_HEAP_ZERO_INIT)) {
+    mallopt(M_BIONIC_ZERO_INIT, 0);
+  }
 
   // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
-  runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
+  runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT;
 
   bool forceEnableGwpAsan = false;
   switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 748b4b4..99fd215 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -16,6 +16,7 @@
 jjaggi@google.com
 roosa@google.com
 per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
 
 # Biometrics
 kchyn@google.com
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index e683306..bb39ea8 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -110,6 +110,8 @@
         optional int32 network_security_config_res = 17;
         optional int32 category = 18;
         optional int32 enable_gwp_asan = 19;
+        optional int32 enable_memtag = 20;
+        optional bool native_heap_zero_init = 21;
     }
     optional Detail detail = 17;
 }
diff --git a/core/proto/android/server/apphibernationservice.proto b/core/proto/android/server/apphibernationservice.proto
new file mode 100644
index 0000000..d341c4b
--- /dev/null
+++ b/core/proto/android/server/apphibernationservice.proto
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+syntax = "proto2";
+package com.android.server.apphibernation;
+
+option java_multiple_files = true;
+
+// Proto for hibernation states for all packages for a user.
+message UserLevelHibernationStatesProto {
+  repeated UserLevelHibernationStateProto hibernation_state = 1;
+}
+
+// Proto for com.android.server.apphibernation.UserLevelState.
+message UserLevelHibernationStateProto {
+  optional string package_name = 1;
+  optional bool hibernated = 2;
+}
+
+// Proto for global hibernation states for all packages.
+message GlobalLevelHibernationStatesProto {
+  repeated GlobalLevelHibernationStateProto hibernation_state = 1;
+}
+
+// Proto for com.android.server.apphibernation.GlobalLevelState
+message GlobalLevelHibernationStateProto {
+  optional string package_name = 1;
+  optional bool hibernated = 2;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
index 0c5a360..4b1ee02 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -191,6 +191,14 @@
     optional string name = 4;
 }
 
+message EnergyConsumerAttributionProto {
+    /** Android ID / Linux UID, the accumulated energy should be attributed to. */
+    optional int32 uid = 1;
+
+    /** Accumulated energy since boot in microwatt-seconds (uWs) for this AID. */
+    optional int64 energy_uws = 2;
+}
+
 /**
  * Energy consumer result:
  * An estimate of energy consumption since boot for the subsystem identified
@@ -205,6 +213,9 @@
 
     /** Accumulated energy since device boot in microwatt-seconds (uWs) */
     optional int64 energy_uws = 3;
+
+    /** Optional attribution per UID for this EnergyConsumer. */
+    repeated EnergyConsumerAttributionProto attribution = 4;
 }
 
 /**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5442c03..dc81bc9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -93,6 +93,7 @@
     <protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
     <protected-broadcast android:name="android.intent.action.USER_INITIALIZE" />
     <protected-broadcast android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
+    <protected-broadcast android:name="android.intent.action.DOMAINS_NEED_VERIFICATION" />
     <protected-broadcast android:name="android.intent.action.OVERLAY_ADDED" />
     <protected-broadcast android:name="android.intent.action.OVERLAY_CHANGED" />
     <protected-broadcast android:name="android.intent.action.OVERLAY_REMOVED" />
@@ -689,10 +690,6 @@
     <!-- Made protected in S (was added in R) -->
     <protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
 
-    <!-- Added in S -->
-    <protected-broadcast android:name="android.app.action.MANAGED_PROFILE_CREATED" />
-    <protected-broadcast android:name="android.app.action.PROVISIONED_MANAGED_DEVICE" />
-
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
@@ -2538,7 +2535,7 @@
     <permission android:name="android.permission.REAL_GET_TASKS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo.
+    <!-- @TestApi Allows an application to start a task from a ActivityManager#RecentTaskInfo.
          @hide -->
     <permission android:name="android.permission.START_TASKS_FROM_RECENTS"
         android:protectionLevel="signature|privileged|recents" />
@@ -2583,6 +2580,10 @@
     <permission android:name="android.permission.CREATE_USERS"
         android:protectionLevel="signature" />
 
+    <!-- @TestApi @hide Allows an application to query user info for all users on the device. -->
+    <permission android:name="android.permission.QUERY_USERS"
+                android:protectionLevel="signature" />
+
     <!-- @hide Allows an application to set the profile owners and the device owner.
          This permission is not available to third party applications.-->
     <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
@@ -4702,6 +4703,26 @@
     <permission android:name="android.permission.BIND_INTENT_FILTER_VERIFIER"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi @hide Domain verification agent package needs to have this permission before the
+         system will trust it to verify domains.
+
+         TODO(159952358): STOPSHIP: This must be updated to the new "internal" protectionLevel
+    -->
+    <permission android:name="android.permission.DOMAIN_VERIFICATION_AGENT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi @hide Must be required by the domain verification agent's intent
+         BroadcastReceiver, to ensure that only the system can interact with it.
+    -->
+    <permission android:name="android.permission.BIND_DOMAIN_VERIFICATION_AGENT"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi @hide Allows an app like Settings to update the user's grants to what domains
+         an app is allowed to automatically open.
+    -->
+    <permission android:name="android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows applications to access serial ports via the SerialManager.
          @hide -->
     <permission android:name="android.permission.SERIAL_PORT"
diff --git a/core/res/res/color-car/car_borderless_button_text_color.xml b/core/res/res/color-car/car_borderless_button_text_color.xml
index 1cdd6cd..0a86e40 100644
--- a/core/res/res/color-car/car_borderless_button_text_color.xml
+++ b/core/res/res/color-car/car_borderless_button_text_color.xml
@@ -16,5 +16,6 @@
 <!-- Default text colors for car buttons when enabled/disabled. -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:color="@*android:color/car_grey_700" android:state_enabled="false"/>
+    <item android:color="@*android:color/car_grey_700" android:state_ux_restricted="true"/>
     <item android:color="?android:attr/colorButtonNormal"/>
 </selector>
diff --git a/core/res/res/color-car/car_switch_track.xml b/core/res/res/color-car/car_switch_track.xml
new file mode 100644
index 0000000..8ca67dd
--- /dev/null
+++ b/core/res/res/color-car/car_switch_track.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<!-- copy of switch_track_material, but with a ux restricted state -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:color="?attr/colorForeground"
+          android:alpha="?attr/disabledAlpha" />
+    <item android:state_ux_restricted="true"
+          android:color="?attr/colorForeground"
+          android:alpha="?attr/disabledAlpha" />
+    <item android:state_checked="true"
+          android:color="?attr/colorControlActivated" />
+    <item android:color="?attr/colorForeground" />
+</selector>
diff --git a/core/res/res/drawable-car/car_button_background.xml b/core/res/res/drawable-car/car_button_background.xml
index e568aeb..13b0ec1 100644
--- a/core/res/res/drawable-car/car_button_background.xml
+++ b/core/res/res/drawable-car/car_button_background.xml
@@ -25,6 +25,22 @@
                     android:color="#0059B3"/>
         </shape>
     </item>
+    <item android:state_focused="true" android:state_pressed="true" android:state_ux_restricted="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="@*android:dimen/car_button_radius"/>
+            <solid android:color="@*android:color/car_grey_300"/>
+            <stroke android:width="4dp"
+                    android:color="#0059B3"/>
+        </shape>
+    </item>
+    <item android:state_focused="true" android:state_ux_restricted="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="@*android:dimen/car_button_radius"/>
+            <solid android:color="@*android:color/car_grey_300"/>
+            <stroke android:width="8dp"
+                    android:color="#0059B3"/>
+        </shape>
+    </item>
     <item android:state_focused="true" android:state_pressed="true">
         <shape android:shape="rectangle">
             <corners android:radius="@*android:dimen/car_button_radius"/>
@@ -47,6 +63,12 @@
             <solid android:color="@*android:color/car_grey_300"/>
         </shape>
     </item>
+    <item android:state_ux_restricted="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="@*android:dimen/car_button_radius"/>
+            <solid android:color="@*android:color/car_grey_300"/>
+        </shape>
+    </item>
     <item>
         <ripple android:color="?android:attr/colorControlHighlight">
             <item>
diff --git a/core/res/res/drawable-car/car_switch_track.xml b/core/res/res/drawable-car/car_switch_track.xml
index cb0b9be..51e9f7e 100644
--- a/core/res/res/drawable-car/car_switch_track.xml
+++ b/core/res/res/drawable-car/car_switch_track.xml
@@ -41,7 +41,7 @@
       android:right="@dimen/car_switch_track_margin_size">
     <shape
         android:shape="rectangle"
-        android:tint="@color/switch_track_material">
+        android:tint="@color/car_switch_track">
       <corners android:radius="7dp" />
       <solid android:color="@color/white_disabled_material" />
       <size android:height="14dp" />
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index 39e1bbb..9e1692f 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -22,7 +22,8 @@
     android:importantForAccessibility="noHideDescendants"
     android:clickable="true">
 
-    <ImageView android:id="@+id/work_widget_app_icon"
+    <com.android.internal.widget.DisableImageView
+        android:id="@+id/work_widget_app_icon"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a8ef2cf..14df775 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -87,6 +87,14 @@
              theme does not set this value, meaning it is based on whether the
              window is floating. -->
         <attr name="backgroundDimEnabled" format="boolean" />
+        <!-- When windowBlurBehindEnabled is set, this is the amount of blur to apply
+             behind the window. The range is from 0, which means no blur, to 150.  -->
+        <attr name="windowBlurBehindRadius" format="dimension"/>
+        <!-- If set, everything behind the window will be blurred with radius
+             windowBackgroundBlurRadius. -->
+        <attr name="windowBlurBehindEnabled" format="boolean" />
+
+
         <!-- Color of background imagery used for popup windows. -->
         <attr name="colorPopupBackground" format="color" />
         <!-- Color used for list divider. -->
@@ -1964,6 +1972,8 @@
         <attr name="textColor" />
         <attr name="backgroundDimEnabled" />
         <attr name="backgroundDimAmount" />
+        <attr name="windowBlurBehindEnabled" />
+        <attr name="windowBlurBehindRadius" />
         <attr name="windowActionBar" />
         <attr name="windowActionModeOverlay" />
         <attr name="windowActionBarOverlay" />
@@ -2181,10 +2191,6 @@
              the decor view. -->
         <attr name="windowLightNavigationBar" format="boolean" />
 
-        <!-- @hide -->
-        <attr name="windowBackgroundBlurRadius" format="dimension"/>
-
-
         <!-- Controls how the window is laid out if there is a {@code DisplayCutout}.
         <p>
         Defaults to {@code default}.
@@ -8494,6 +8500,9 @@
              interaction requests from an Activity. This flag is new in
              {@link android.os.Build.VERSION_CODES#N} and not used in previous versions. -->
         <attr name="supportsLocalInteraction" format="boolean" />
+        <!-- The service that provides {@link android.service.voice.HotwordDetectionService}.
+             @hide @SystemApi -->
+        <attr name="hotwordDetectionService" format="string" />
     </declare-styleable>
 
     <!-- Use <code>voice-enrollment-application</code>
diff --git a/core/res/res/values/attrs_car.xml b/core/res/res/values/attrs_car.xml
new file mode 100644
index 0000000..6bfea97
--- /dev/null
+++ b/core/res/res/values/attrs_car.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<!-- Formatting note: terminate all comments with a period, to avoid breaking
+     the documentation output. To suppress comment lines from the documentation
+     output, insert an eat-comment element after the comment lines.
+-->
+
+<resources>
+     <attr name="state_ux_restricted" format="boolean"/>
+</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c16588c..b4e580a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1580,6 +1580,13 @@
        <enum name="always" value="1" />
     </attr>
 
+    <attr name="memtagMode">
+       <enum name="default" value="-1" />
+       <enum name="off" value="0" />
+       <enum name="async" value="1" />
+       <enum name="sync" value="2" />
+    </attr>
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -1847,6 +1854,10 @@
 
         <attr name="gwpAsanMode" />
 
+        <attr name="memtagMode" />
+
+        <attr name="nativeHeapZeroInit" format="boolean" />
+
         <!-- @hide no longer used, kept to preserve padding -->
         <attr name="allowAutoRevokePermissionsExemption" format="boolean" />
 
@@ -2417,6 +2428,8 @@
         <!-- Required name of the process that is allowed -->
         <attr name="process" />
         <attr name="gwpAsanMode" />
+        <attr name="memtagMode" />
+        <attr name="nativeHeapZeroInit" />
     </declare-styleable>
 
     <!-- The <code>deny-permission</code> tag specifies that a permission is to be denied
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 4f920da..20a5d37 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -255,7 +255,7 @@
     <color name="system_main_400">#909090</color>
     <!-- Shade of the main system color at 50% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_main_500">#777777</color>
+    <color name="system_main_500">#757575</color>
     <!-- Shade of the main system color at 40% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
     <color name="system_main_600">#5e5e5e</color>
@@ -292,7 +292,7 @@
     <color name="system_accent_400">#1fa293</color>
     <!-- Shade of the accent system color at 50% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent_500">#00877a</color>
+    <color name="system_accent_500">#008377</color>
     <!-- Shade of the accent system color at 40% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
     <color name="system_accent_600">#006d61</color>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 6e9eb82..c8cccc4 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -17,9 +17,9 @@
 <!-- Colors specific to DeviceDefault themes. These are mostly pass-throughs to enable
      overlaying new theme colors. -->
 <resources>
-    <color name="primary_device_default_dark">@color/system_main_900</color>
+    <color name="primary_device_default_dark">@color/system_main_800</color>
     <color name="primary_device_default_light">@color/system_main_50</color>
-    <color name="primary_device_default_settings">@color/system_main_900</color>
+    <color name="primary_device_default_settings">@color/system_main_800</color>
     <color name="primary_device_default_settings_light">@color/primary_device_default_light</color>
     <color name="primary_dark_device_default_dark">@color/primary_device_default_dark</color>
     <color name="primary_dark_device_default_light">@color/primary_device_default_light</color>
@@ -37,17 +37,24 @@
     <color name="accent_device_default_dark">@color/system_accent_200</color>
     <color name="accent_device_default">@color/accent_device_default_light</color>
 
-    <color name="background_device_default_dark">@color/system_main_900</color>
+    <color name="background_device_default_dark">@color/system_main_800</color>
     <color name="background_device_default_light">@color/system_main_50</color>
-    <color name="background_floating_device_default_dark">@color/system_main_800</color>
+    <color name="background_floating_device_default_dark">@color/system_main_900</color>
     <color name="background_floating_device_default_light">@color/system_main_100</color>
 
+    <color name="text_color_primary_device_default_light">@color/system_main_900</color>
+    <color name="text_color_primary_device_default_dark">@color/system_main_50</color>
+    <color name="text_color_secondary_device_default_light">@color/system_main_700</color>
+    <color name="text_color_secondary_device_default_dark">@color/system_main_200</color>
+    <color name="text_color_tertiary_device_default_light">@color/system_main_500</color>
+    <color name="text_color_tertiary_device_default_dark">@color/system_main_400</color>
+
     <!-- Error color -->
     <color name="error_color_device_default_dark">@color/error_color_material_dark</color>
     <color name="error_color_device_default_light">@color/error_color_material_light</color>
 
-    <color name="list_divider_color_light">#ffdadce0</color>
-    <color name="list_divider_color_dark">#85ffffff</color>
+    <color name="list_divider_color_light">@color/system_main_500</color>
+    <color name="list_divider_color_dark">@color/system_main_400</color>
     <color name="list_divider_opacity_device_default_light">@android:color/white</color>
     <color name="list_divider_opacity_device_default_dark">@android:color/white</color>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 416bc84..3790aa4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -663,9 +663,15 @@
          The default is false. -->
     <bool name="config_lidControlsSleep">false</bool>
 
-    <!-- The device state (supplied by DeviceStateManager) that should be treated as folded by the
-         display fold controller. Default is DeviceStateManager.INVALID_DEVICE_STATE. -->
-    <integer name="config_foldedDeviceState">-1</integer>
+    <!-- The device states (supplied by DeviceStateManager) that should be treated as folded by the
+         display fold controller. Default is empty. -->
+    <integer-array name="config_foldedDeviceStates">
+        <!-- Example:
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        -->
+    </integer-array>
 
     <!-- Indicate the display area rect for foldable devices in folded state. -->
     <string name="config_foldedArea"></string>
@@ -825,28 +831,21 @@
         <!-- B y-intercept --> <item>-0.198650895</item>
     </string-array>
 
-    <string-array name="config_reduceBrightColorsCoefficientsNative">
-        <!-- R a-coefficient --> <item>-0.691218457</item>
-        <!-- R b-coefficient --> <item>0.050135153</item>
-        <!-- R y-intercept --> <item>0.917684143</item>
-        <!-- G a-coefficient --> <item>-0.691218457</item>
-        <!-- G b-coefficient --> <item>0.050135153</item>
-        <!-- G y-intercept --> <item>0.917684143</item>
-        <!-- B a-coefficient --> <item>-0.691218457</item>
-        <!-- B b-coefficient --> <item>0.050135153</item>
-        <!-- B y-intercept --> <item>0.917684143</item>
+    <!-- Control whether bright color reduction is available. This should only be enabled on devices
+         that have a HWC implementation that can apply the matrix passed to setColorTransform
+         without impacting power, performance, and app compatibility (e.g. protected content). -->
+    <bool name="config_reduceBrightColorsAvailable">@bool/config_setColorTransformAccelerated</bool>
+
+    <string-array name="config_reduceBrightColorsCoefficientsNonlinear">
+        <!-- a-coefficient --> <item>-0.4429953456</item>
+        <!-- b-coefficient --> <item>-0.2434077725</item>
+        <!-- y-intercept --> <item>0.9809063061</item>
     </string-array>
 
     <string-array name="config_reduceBrightColorsCoefficients">
-        <!-- R a-coefficient --> <item>0.00000000000000154</item>
-        <!-- R b-coefficient --> <item>-1.0</item>
-        <!-- R y-intercept --> <item>1.045977011</item>
-        <!-- G a-coefficient --> <item>0.00000000000000224</item>
-        <!-- G b-coefficient --> <item>-1.0</item>
-        <!-- G y-intercept --> <item>1.045977011</item>
-        <!-- B a-coefficient --> <item>0.0000000000000022</item>
-        <!-- B b-coefficient --> <item>-1.0</item>
-        <!-- B y-intercept --> <item>1.045977011</item>
+        <!-- a-coefficient --> <item>-0.000000000000001</item>
+        <!-- b-coefficient --> <item>-0.955555555555554</item>
+        <!-- y-intercept --> <item>1.000000000000000</item>
     </string-array>
 
     <!-- Boolean indicating whether display white balance is supported. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2381fc6..30cfb89 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3048,8 +3048,8 @@
     <public name="allowClickWhenDisabled" />
     <public name="windowLayoutAffinity" />
     <public name="canPauseRecording" />
-    <!-- @hide -->
-    <public name="windowBackgroundBlurRadius"/>
+    <public name="windowBlurBehindRadius"/>
+    <public name="windowBlurBehindEnabled"/>
     <public name="requireDeviceScreenOn" />
     <public name="pathSuffix" />
     <public name="sspSuffix" />
@@ -3057,6 +3057,10 @@
     <public name="sspAdvancedPattern" />
     <public name="fontProviderSystemFontFamily" />
     <public name="hand_second" />
+    <public name="memtagMode" />
+    <public name="nativeHeapZeroInit" />
+    <!-- @hide @SystemApi -->
+    <public name="hotwordDetectionService" />
   </public-group>
 
   <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 672aec6..80163b1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3193,8 +3193,9 @@
   <java-symbol type="integer" name="config_nightDisplayColorTemperatureMax" />
   <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficients" />
   <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" />
+  <java-symbol type="bool" name="config_reduceBrightColorsAvailable" />
   <java-symbol type="array" name="config_reduceBrightColorsCoefficients" />
-  <java-symbol type="array" name="config_reduceBrightColorsCoefficientsNative" />
+  <java-symbol type="array" name="config_reduceBrightColorsCoefficientsNonlinear" />
   <java-symbol type="array" name="config_availableColorModes" />
   <java-symbol type="array" name="config_mappedColorModes" />
   <java-symbol type="string" name="config_vendorColorModesRestoreHint" />
@@ -3731,7 +3732,7 @@
   <java-symbol type="string" name="config_customCountryDetector" />
 
   <!-- For Foldables -->
-  <java-symbol type="integer" name="config_foldedDeviceState" />
+  <java-symbol type="array" name="config_foldedDeviceStates" />
   <java-symbol type="string" name="config_foldedArea" />
 
   <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index dff6e87..ee17f6f 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -219,6 +219,9 @@
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorPopupBackground">?attr/colorBackgroundFloating</item>
         <item name="panelColorBackground">?attr/colorBackgroundFloating</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
     </style>
 
     <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -230,6 +233,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -258,6 +264,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -288,6 +297,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -317,6 +329,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -363,6 +378,9 @@
         <item name="colorError">@color/error_color_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -384,6 +402,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -411,6 +432,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -439,6 +463,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -483,6 +510,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -512,6 +542,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -539,6 +572,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -568,6 +604,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -596,6 +635,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -624,6 +666,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -652,6 +697,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -680,6 +728,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -712,6 +763,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Text styles -->
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
@@ -741,6 +795,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -767,6 +824,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -949,6 +1009,9 @@
         <item name="colorError">@color/error_color_device_default_light</item>
         <item name="colorBackground">@color/background_device_default_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
         <item name="colorPopupBackground">?attr/colorBackgroundFloating</item>
         <item name="panelColorBackground">?attr/colorBackgroundFloating</item>
     </style>
@@ -961,6 +1024,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -988,6 +1054,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1016,6 +1085,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1046,6 +1118,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1075,6 +1150,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1123,6 +1201,9 @@
         <item name="colorError">@color/error_color_device_default_light</item>
         <item name="colorBackground">@color/background_device_default_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
@@ -1143,6 +1224,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1173,6 +1257,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1204,6 +1291,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1236,6 +1326,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -1250,6 +1343,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
     </style>
 
     <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -1263,6 +1359,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1295,6 +1394,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1325,6 +1427,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1354,6 +1459,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1382,6 +1490,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1410,6 +1521,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1436,6 +1550,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1552,6 +1669,9 @@
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1579,6 +1699,9 @@
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
@@ -1616,6 +1739,9 @@
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1646,6 +1772,9 @@
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
+        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
index e0d159b..9e6827c 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
@@ -63,7 +63,9 @@
     private final List<Entry> mEntries = new ArrayList<>();
 
     public BatteryConsumerData(Context context, BatteryStatsHelper batteryStatsHelper,
-            BatteryUsageStats batteryUsageStats, String batteryConsumerId) {
+            List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId) {
+        BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0);
+        BatteryUsageStats powerProfileModeledUsageStats = batteryUsageStatsList.get(1);
         List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
         BatteryStats batteryStats = batteryStatsHelper.getStats();
 
@@ -142,16 +144,22 @@
         }
 
         BatteryConsumer requestedBatteryConsumer = null;
-        double totalModeledCpuPowerMah = 0;
 
         for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
             if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
                 requestedBatteryConsumer = consumer;
             }
+        }
 
-            totalModeledCpuPowerMah += consumer.getConsumedPowerForCustomComponent(
-                    BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
-                            + BatteryConsumer.POWER_COMPONENT_CPU);
+        double totalModeledCpuPowerMah = 0;
+        BatteryConsumer requestedBatteryConsumerPowerProfileModeled = null;
+        for (BatteryConsumer consumer : powerProfileModeledUsageStats.getUidBatteryConsumers()) {
+            if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
+                requestedBatteryConsumerPowerProfileModeled = consumer;
+            }
+
+            totalModeledCpuPowerMah += consumer.getConsumedPower(
+                    BatteryConsumer.POWER_COMPONENT_CPU);
         }
 
         if (requestedBatterySipper == null) {
@@ -196,11 +204,10 @@
             addEntry("CPU", EntryType.POWER,
                     requestedBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU),
                     totalCpuPowerMah);
-            if (totalModeledCpuPowerMah != 0) {
+            if (requestedBatteryConsumerPowerProfileModeled != null) {
                 addEntry("CPU (modeled)", EntryType.POWER,
-                        requestedBatteryConsumer.getConsumedPowerForCustomComponent(
-                                BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
-                                        + BatteryConsumer.POWER_COMPONENT_CPU),
+                        requestedBatteryConsumerPowerProfileModeled.getConsumedPower(
+                                BatteryConsumer.POWER_COMPONENT_CPU),
                         totalModeledCpuPowerMah);
             }
         } else {
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
index 87a175a..4ead8ee 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
@@ -68,7 +68,7 @@
     private ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult(
             BatteryConsumerPickerActivity.CONTRACT, this::onApplicationSelected);
     private BatteryStatsHelper mBatteryStatsHelper;
-    private BatteryUsageStats mBatteryUsageStats;
+    private List<BatteryUsageStats> mBatteryUsageStats;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -188,7 +188,8 @@
         }
     }
 
-    private static class BatteryUsageStatsLoader extends AsyncLoaderCompat<BatteryUsageStats> {
+    private static class BatteryUsageStatsLoader extends
+            AsyncLoaderCompat<List<BatteryUsageStats>> {
         private final BatteryStatsManager mBatteryStatsManager;
 
         BatteryUsageStatsLoader(Context context) {
@@ -197,33 +198,38 @@
         }
 
         @Override
-        public BatteryUsageStats loadInBackground() {
-            final BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
-                    .includeModeled()
-                    .build();
-            return mBatteryStatsManager.getBatteryUsageStats(query);
+        public List<BatteryUsageStats> loadInBackground() {
+            final BatteryUsageStatsQuery queryDefault =
+                    new BatteryUsageStatsQuery.Builder().build();
+            final BatteryUsageStatsQuery queryPowerProfileModeledOnly =
+                    new BatteryUsageStatsQuery.Builder()
+                            .powerProfileModeledOnly()
+                            .build();
+            return mBatteryStatsManager.getBatteryUsageStats(
+                    List.of(queryDefault, queryPowerProfileModeledOnly));
         }
 
         @Override
-        protected void onDiscardResult(BatteryUsageStats result) {
+        protected void onDiscardResult(List<BatteryUsageStats> result) {
         }
     }
 
-    private class BatteryUsageStatsLoaderCallbacks implements LoaderCallbacks<BatteryUsageStats> {
+    private class BatteryUsageStatsLoaderCallbacks
+            implements LoaderCallbacks<List<BatteryUsageStats>> {
         @NonNull
         @Override
-        public Loader<BatteryUsageStats> onCreateLoader(int id, Bundle args) {
+        public Loader<List<BatteryUsageStats>> onCreateLoader(int id, Bundle args) {
             return new BatteryUsageStatsLoader(BatteryStatsViewerActivity.this);
         }
 
         @Override
-        public void onLoadFinished(@NonNull Loader<BatteryUsageStats> loader,
-                BatteryUsageStats batteryUsageStats) {
+        public void onLoadFinished(@NonNull Loader<List<BatteryUsageStats>> loader,
+                List<BatteryUsageStats> batteryUsageStats) {
             onBatteryUsageStatsLoaded(batteryUsageStats);
         }
 
         @Override
-        public void onLoaderReset(@NonNull Loader<BatteryUsageStats> loader) {
+        public void onLoaderReset(@NonNull Loader<List<BatteryUsageStats>> loader) {
         }
     }
 
@@ -232,7 +238,7 @@
         onBatteryStatsDataLoaded();
     }
 
-    private void onBatteryUsageStatsLoaded(BatteryUsageStats batteryUsageStats) {
+    private void onBatteryUsageStatsLoaded(List<BatteryUsageStats> batteryUsageStats) {
         mBatteryUsageStats = batteryUsageStats;
         onBatteryStatsDataLoaded();
     }
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 151a320..f31233b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -35,8 +35,6 @@
             android:label="@string/permlab_testDenied"
             android:description="@string/permdesc_testDenied" />
 
-    <uses-permission android:name="com.android.frameworks.coretests.permission.TEST_GRANTED" />
-
     <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
     <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
     <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED" />
@@ -78,6 +76,7 @@
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SMS"/>
+    <uses-permission android:name="android.permission.TEST_GRANTED" />
     <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
     <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
     <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
@@ -148,6 +147,9 @@
     <!-- WindowMetricsTest permissions -->
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 
+    <!-- WindowContextTest permissions -->
+    <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+
     <!-- Allow use of PendingIntent.getIntent() -->
     <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
 
@@ -1456,7 +1458,6 @@
             </intent-filter>
         </receiver>
         <receiver android:name="android.app.activity.RemoteGrantedReceiver"
-                android:process=":remoteReceiver"
                 android:permission="com.android.frameworks.coretests.permission.TEST_GRANTED"
                 android:exported="true">
             <intent-filter android:priority="2">
@@ -1464,7 +1465,6 @@
             </intent-filter>
         </receiver>
         <receiver android:name="android.app.activity.RemoteDeniedReceiver"
-                android:process=":remoteReceiver"
                 android:permission="com.android.frameworks.coretests.permission.TEST_DENIED"
                 android:exported="true">
             <intent-filter android:priority="2">
diff --git a/core/tests/coretests/assets/fonts/OWNERS b/core/tests/coretests/assets/fonts/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/OWNERS b/core/tests/coretests/assets/fonts_for_family_selection/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/res/font/OWNERS b/core/tests/coretests/res/font/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/res/font/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java
index dcf5e02..da7304e 100644
--- a/core/tests/coretests/src/android/app/WindowContextTest.java
+++ b/core/tests/coretests/src/android/app/WindowContextTest.java
@@ -18,29 +18,38 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.display.DisplayManager;
+import android.os.Binder;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.view.Display;
 import android.view.IWindowManager;
 import android.view.View;
 import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerImpl;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Tests for {@link WindowContext}
  *
@@ -54,20 +63,14 @@
 @SmallTest
 @Presubmit
 public class WindowContextTest {
+    @Rule
+    public ActivityTestRule<EmptyActivity> mActivityRule =
+            new ActivityTestRule<>(EmptyActivity.class, false /* initialTouchMode */,
+                    false /* launchActivity */);
+
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private final WindowContext mWindowContext = createWindowContext();
-
-    @Test
-    public void testWindowContextRelease_doRemoveWindowToken() throws Throwable {
-        final IBinder token = mWindowContext.getWindowContextToken();
-        final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
-
-        assertTrue("Token must be registered to WMS", wms.isWindowToken(token));
-
-        mWindowContext.release();
-
-        assertFalse("Token must be unregistered to WMS", wms.isWindowToken(token));
-    }
+    private final IWindowManager mWms = WindowManagerGlobal.getWindowManagerService();
 
     @Test
     public void testCreateWindowContextWindowManagerAttachClientToken() {
@@ -83,12 +86,133 @@
         assertEquals(mWindowContext.getWindowContextToken(), params.mWindowContextToken);
     }
 
+    /**
+     * Test the {@link WindowContext} life cycle behavior to add a new window token:
+     * <ul>
+     *  <li>The window token is created before adding the first view.</li>
+     *  <li>The window token is registered after adding the first view.</li>
+     *  <li>The window token is removed after {@link WindowContext}'s release.</li>
+     * </ul>
+     */
+    @Test
+    public void testCreateWindowContextNewTokenFromClient() throws Throwable {
+        final IBinder token = mWindowContext.getWindowContextToken();
+
+        // Test that the window token is not created yet.
+        assertFalse("Token must not be registered until adding the first window",
+                mWms.isWindowToken(token));
+
+        final WindowManager.LayoutParams params =
+                new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+        final View testView = new View(mWindowContext);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        testView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                latch.countDown();
+            }
+
+            @Override
+            public void onViewDetachedFromWindow(View v) {}
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+            assertEquals(token, params.mWindowContextToken);
+        });
+
+
+        assertTrue(latch.await(4, TimeUnit.SECONDS));
+
+
+        // Verify that the window token of the window context is created after first addView().
+        assertTrue("Token must exist after adding the first view.",
+                mWms.isWindowToken(token));
+
+        mWindowContext.release();
+
+        // After the window context's release, the window token is also removed.
+        assertFalse("Token must be removed after release.", mWms.isWindowToken(token));
+    }
+
+    /**
+     * Verifies the behavior when window context attaches an {@link Activity} by override
+     * {@link WindowManager.LayoutParams#token}.
+     *
+     * The window context token should be overridden to
+     * {@link android.view.WindowManager.LayoutParams} and the {@link Activity}'s token must
+     * not be removed regardless of the release of window context.
+     */
+    @Test
+    public void testCreateWindowContext_AttachActivity_TokenNotRemovedAfterRelease()
+            throws Throwable {
+        mActivityRule.launchActivity(new Intent());
+        final Activity activity = mActivityRule.getActivity();
+        final WindowManager.LayoutParams params = activity.getWindow().getAttributes();
+
+        final WindowContext windowContext = createWindowContext(params.type);
+        final IBinder token = windowContext.getWindowContextToken();
+
+        final View testView = new View(windowContext);
+
+        mInstrumentation.runOnMainSync(() -> {
+            windowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+            assertEquals(token, params.mWindowContextToken);
+        });
+        windowContext.release();
+
+        // Even if the window context is released, the activity should still exist.
+        assertTrue("Token must exist even if the window context is released.",
+                mWms.isWindowToken(activity.getActivityToken()));
+    }
+
+    /**
+     * Verifies the behavior when window context attaches an existing token by override
+     * {@link WindowManager.LayoutParams#token}.
+     *
+     * The window context token should be overridden to
+     * {@link android.view.WindowManager.LayoutParams} and the {@link Activity}'s token must not be
+     * removed regardless of release of window context.
+     */
+    @Test
+    public void testCreateWindowContext_AttachWindowToken_TokenNotRemovedAfterRelease()
+            throws Throwable {
+        final WindowContext windowContext = createWindowContext(TYPE_INPUT_METHOD);
+        final IBinder token = windowContext.getWindowContextToken();
+
+        final IBinder existingToken = new Binder();
+        mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId());
+
+        final WindowManager.LayoutParams params =
+                new WindowManager.LayoutParams(TYPE_INPUT_METHOD);
+        params.token = existingToken;
+        final View testView = new View(windowContext);
+
+        mInstrumentation.runOnMainSync(() -> {
+            windowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+            assertEquals(token, params.mWindowContextToken);
+        });
+        windowContext.release();
+
+        // Even if the window context is released, the existing token should still exist.
+        assertTrue("Token must exist even if the window context is released.",
+                mWms.isWindowToken(existingToken));
+
+        mWms.removeWindowToken(existingToken, DEFAULT_DISPLAY);
+    }
+
     private WindowContext createWindowContext() {
+        return createWindowContext(TYPE_APPLICATION_OVERLAY);
+    }
+
+    private WindowContext createWindowContext(@WindowType int type) {
         final Context instContext = mInstrumentation.getTargetContext();
         final Display display = instContext.getSystemService(DisplayManager.class)
                 .getDisplay(DEFAULT_DISPLAY);
-        final Context context = instContext.createDisplayContext(display);
-        return new WindowContext(context, TYPE_APPLICATION_OVERLAY,
-                null /* options */);
+        return (WindowContext) instContext.createWindowContext(display, type,  null /* options */);
     }
 }
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index d79c2fe..0f81896 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -56,8 +56,6 @@
             "com.android.frameworks.coretests.activity.BROADCAST_MULTI";
     public static final String BROADCAST_ABORT =
             "com.android.frameworks.coretests.activity.BROADCAST_ABORT";
-    public static final String BROADCAST_RESULT =
-            "com.android.frameworks.coretests.activity.BROADCAST_RESULT";
 
     public static final String BROADCAST_STICKY1 =
             "com.android.frameworks.coretests.activity.BROADCAST_STICKY1";
@@ -108,14 +106,7 @@
     }
 
     public Intent makeBroadcastIntent(String action) {
-        return makeBroadcastIntent(action, false);
-    }
-
-    public Intent makeBroadcastIntent(String action, boolean makeImplicit) {
         Intent intent = new Intent(action, null);
-        if (makeImplicit) {
-            intent.addFlags(intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-        }
         intent.putExtra("caller", mCallTarget);
         return intent;
     }
@@ -286,7 +277,7 @@
             map.putString("foo", "you");
             map.putString("remove", "me");
             getContext().sendOrderedBroadcast(
-                    makeBroadcastIntent(BROADCAST_RESULT, true),
+                    new Intent("com.android.frameworks.coretests.activity.BROADCAST_RESULT"),
                     null, broadcastReceiver, null, 1, "foo", map);
             while (!broadcastReceiver.mHaveResult) {
                 try {
@@ -433,13 +424,10 @@
 
     public void testLocalReceivePermissionGranted() throws Exception {
         setExpectedReceivers(new String[]{RECEIVER_LOCAL});
-        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL_GRANTED, true));
+        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL_GRANTED));
         waitForResultOrThrow(BROADCAST_TIMEOUT);
     }
 
-    /*
-    // TODO: multi-package test b/c self-target broadcasts are always allowed
-    // even when gated on ungranted permissions
     public void testLocalReceivePermissionDenied() throws Exception {
         setExpectedReceivers(new String[]{RECEIVER_RESULTS});
 
@@ -450,17 +438,16 @@
         };
 
         getContext().sendOrderedBroadcast(
-                makeBroadcastIntent(BROADCAST_LOCAL_DENIED, true),
+                makeBroadcastIntent(BROADCAST_LOCAL_DENIED),
                 null, finish, null, Activity.RESULT_CANCELED,
                 null, null);
         waitForResultOrThrow(BROADCAST_TIMEOUT);
     }
-    */
 
     public void testLocalBroadcastPermissionGranted() throws Exception {
         setExpectedReceivers(new String[]{RECEIVER_LOCAL});
         getContext().sendBroadcast(
-                makeBroadcastIntent(BROADCAST_LOCAL, true),
+                makeBroadcastIntent(BROADCAST_LOCAL),
                 PERMISSION_GRANTED);
         waitForResultOrThrow(BROADCAST_TIMEOUT);
     }
@@ -475,7 +462,7 @@
         };
 
         getContext().sendOrderedBroadcast(
-                makeBroadcastIntent(BROADCAST_LOCAL, true),
+                makeBroadcastIntent(BROADCAST_LOCAL),
                 PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
                 null, null);
         waitForResultOrThrow(BROADCAST_TIMEOUT);
@@ -483,13 +470,10 @@
 
     public void testRemoteReceivePermissionGranted() throws Exception {
         setExpectedReceivers(new String[]{RECEIVER_REMOTE});
-        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE_GRANTED, true));
+        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE_GRANTED));
         waitForResultOrThrow(BROADCAST_TIMEOUT);
     }
 
-    /*
-    // TODO: multi-package test b/c self-target broadcasts are always allowed
-    // even when gated on ungranted permissions
     public void testRemoteReceivePermissionDenied() throws Exception {
         setExpectedReceivers(new String[]{RECEIVER_RESULTS});
 
@@ -500,17 +484,16 @@
         };
 
         getContext().sendOrderedBroadcast(
-                makeBroadcastIntent(BROADCAST_REMOTE_DENIED, true),
+                makeBroadcastIntent(BROADCAST_REMOTE_DENIED),
                 null, finish, null, Activity.RESULT_CANCELED,
                 null, null);
         waitForResultOrThrow(BROADCAST_TIMEOUT);
     }
-    */
 
     public void testRemoteBroadcastPermissionGranted() throws Exception {
         setExpectedReceivers(new String[]{RECEIVER_REMOTE});
         getContext().sendBroadcast(
-                makeBroadcastIntent(BROADCAST_REMOTE, true),
+                makeBroadcastIntent(BROADCAST_REMOTE),
                 PERMISSION_GRANTED);
         waitForResultOrThrow(BROADCAST_TIMEOUT);
     }
@@ -525,7 +508,7 @@
         };
 
         getContext().sendOrderedBroadcast(
-                makeBroadcastIntent(BROADCAST_REMOTE, true),
+                makeBroadcastIntent(BROADCAST_REMOTE),
                 PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
                 null, null);
         waitForResultOrThrow(BROADCAST_TIMEOUT);
@@ -533,13 +516,13 @@
 
     public void testReceiverCanNotRegister() throws Exception {
         setExpectedReceivers(new String[]{RECEIVER_LOCAL});
-        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_REGISTER, true));
+        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_REGISTER));
         waitForResultOrThrow(BROADCAST_TIMEOUT);
     }
 
     public void testReceiverCanNotBind() throws Exception {
         setExpectedReceivers(new String[]{RECEIVER_LOCAL});
-        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_BIND, true));
+        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_BIND));
         waitForResultOrThrow(BROADCAST_TIMEOUT);
     }
 
diff --git a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
index 0b21fa9..7662456 100644
--- a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
+++ b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
@@ -253,16 +253,16 @@
                 sendBroadcast(makeBroadcastIntent(BROADCAST_REGISTERED));
             } else if (BROADCAST_LOCAL.equals(action)) {
                 setExpectedReceivers(new String[]{RECEIVER_LOCAL});
-                sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL, true));
+                sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL));
             } else if (BROADCAST_REMOTE.equals(action)) {
                 setExpectedReceivers(new String[]{RECEIVER_REMOTE});
-                sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE, true));
+                sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE));
             } else if (BROADCAST_ALL.equals(action)) {
                 setExpectedReceivers(new String[]{
                         RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL});
                 registerMyReceiver(new IntentFilter(BROADCAST_ALL));
                 sCallingTest.addIntermediate("after-register");
-                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL, true), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
             } else if (BROADCAST_MULTI.equals(action)) {
                 setExpectedReceivers(new String[]{
                         RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
@@ -277,26 +277,23 @@
                         RECEIVER_REMOTE, RECEIVER_LOCAL});
                 registerMyReceiver(new IntentFilter(BROADCAST_ALL));
                 sCallingTest.addIntermediate("after-register");
-                final Intent allIntent = makeBroadcastIntent(BROADCAST_ALL, true);
-                final Intent localIntent = makeBroadcastIntent(BROADCAST_LOCAL, true);
-                final Intent remoteIntent = makeBroadcastIntent(BROADCAST_REMOTE, true);
-                sendOrderedBroadcast(allIntent, null);
-                sendOrderedBroadcast(allIntent, null);
-                sendOrderedBroadcast(allIntent, null);
-                sendOrderedBroadcast(localIntent, null);
-                sendOrderedBroadcast(remoteIntent, null);
-                sendOrderedBroadcast(localIntent, null);
-                sendOrderedBroadcast(remoteIntent, null);
-                sendOrderedBroadcast(allIntent, null);
-                sendOrderedBroadcast(allIntent, null);
-                sendOrderedBroadcast(allIntent, null);
-                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REPEAT, true), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REPEAT), null);
             } else if (BROADCAST_ABORT.equals(action)) {
                 setExpectedReceivers(new String[]{
                         RECEIVER_REMOTE, RECEIVER_ABORT});
                 registerMyReceiver(new IntentFilter(BROADCAST_ABORT));
                 sCallingTest.addIntermediate("after-register");
-                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ABORT, true), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ABORT), null);
             } else if (BROADCAST_STICKY1.equals(action)) {
                 setExpectedReceivers(new String[]{RECEIVER_REG});
                 setExpectedData(new String[]{DATA_1});
@@ -439,14 +436,7 @@
     }
 
     private Intent makeBroadcastIntent(String action) {
-        return makeBroadcastIntent(action, false);
-    }
-
-    private Intent makeBroadcastIntent(String action, boolean makeImplicit) {
         Intent intent = new Intent(action, null);
-        if (makeImplicit) {
-            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-        }
         intent.putExtra("caller", mCallTarget);
         return intent;
     }
diff --git a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
index 16ea73f..2120a1d 100644
--- a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
+++ b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
@@ -23,7 +23,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 
-public class LocalDeniedReceiver extends BroadcastReceiver {
+class LocalDeniedReceiver extends BroadcastReceiver {
     public LocalDeniedReceiver() {
     }
 
diff --git a/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
index 5c1ded9..7c89346 100644
--- a/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
+++ b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
@@ -23,7 +23,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 
-public class RemoteDeniedReceiver extends BroadcastReceiver {
+class RemoteDeniedReceiver extends BroadcastReceiver {
     public RemoteDeniedReceiver() {
     }
 
diff --git a/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
new file mode 100644
index 0000000..be38260
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 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.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.ContentValues;
+import android.os.Parcel;
+import android.provider.SimPhonebookContract.SimRecords.NameValidationResult;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SimPhonebookContractTest {
+
+    @Test
+    public void getContentUri_invalidEfType_throwsIllegalArgumentException() {
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getContentUri(1, 100)
+        );
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getContentUri(1, -1)
+        );
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getContentUri(1,
+                        SimPhonebookContract.ElementaryFiles.EF_UNKNOWN)
+        );
+    }
+
+    @Test
+    public void getItemUri_invalidEfType_throwsIllegalArgumentException() {
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getItemUri(1, 100, 1)
+        );
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getItemUri(1, -1, 1)
+        );
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getItemUri(1,
+                        SimPhonebookContract.ElementaryFiles.EF_UNKNOWN, 1)
+        );
+    }
+
+    @Test
+    public void getItemUri_invalidRecordIndex_throwsIllegalArgumentException() {
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getItemUri(1,
+                        SimPhonebookContract.ElementaryFiles.EF_ADN, 0)
+        );
+        assertThrows(IllegalArgumentException.class, () ->
+                SimPhonebookContract.SimRecords.getItemUri(1,
+                        SimPhonebookContract.ElementaryFiles.EF_ADN, -1)
+        );
+    }
+
+    @Test
+    public void nameValidationResult_isValid_validNames() {
+        assertThat(new NameValidationResult("", "", 0, 1).isValid()).isTrue();
+        assertThat(new NameValidationResult("a", "a", 1, 1).isValid()).isTrue();
+        assertThat(new NameValidationResult("First Last", "First Last", 10, 10).isValid()).isTrue();
+        assertThat(
+                new NameValidationResult("First Last", "First Last", 10, 100).isValid()).isTrue();
+    }
+
+    @Test
+    public void nameValidationResult_isValid_invalidNames() {
+        assertThat(new NameValidationResult("", "", 0, 0).isValid()).isFalse();
+        assertThat(new NameValidationResult("ab", "ab", 2, 1).isValid()).isFalse();
+        NameValidationResult unsupportedCharactersResult = new NameValidationResult("A_b_c",
+                "A b c", 5, 5);
+        assertThat(unsupportedCharactersResult.isValid()).isFalse();
+        assertThat(unsupportedCharactersResult.isSupportedCharacter(0)).isTrue();
+        assertThat(unsupportedCharactersResult.isSupportedCharacter(1)).isFalse();
+        assertThat(unsupportedCharactersResult.isSupportedCharacter(2)).isTrue();
+        assertThat(unsupportedCharactersResult.isSupportedCharacter(3)).isFalse();
+        assertThat(unsupportedCharactersResult.isSupportedCharacter(4)).isTrue();
+    }
+
+    @Test
+    public void nameValidationResult_parcel() {
+        ContentValues values = new ContentValues();
+        values.put("name", "Name");
+        values.put("phone_number", "123");
+
+        NameValidationResult result;
+        Parcel parcel = Parcel.obtain();
+        try {
+            parcel.writeParcelable(new NameValidationResult("name", "sanitized name", 1, 2), 0);
+            parcel.setDataPosition(0);
+            result = parcel.readParcelable(NameValidationResult.class.getClassLoader());
+        } finally {
+            parcel.recycle();
+        }
+
+        assertThat(result.getName()).isEqualTo("name");
+        assertThat(result.getSanitizedName()).isEqualTo("sanitized name");
+        assertThat(result.getEncodedLength()).isEqualTo(1);
+        assertThat(result.getMaxEncodedLength()).isEqualTo(2);
+    }
+}
+
diff --git a/core/tests/coretests/src/com/android/internal/listeners/ListenerTransportMultiplexerTest.java b/core/tests/coretests/src/com/android/internal/listeners/ListenerTransportMultiplexerTest.java
deleted file mode 100644
index 127ecfb..0000000
--- a/core/tests/coretests/src/com/android/internal/listeners/ListenerTransportMultiplexerTest.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2020 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.internal.listeners;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.os.Handler;
-import android.os.Looper;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import junit.framework.TestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicReference;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class ListenerTransportMultiplexerTest extends TestCase {
-
-    TestMultiplexer mMultiplexer;
-
-    @Before
-    public void setUp() {
-        mMultiplexer = new TestMultiplexer();
-    }
-
-    @Test
-    public void testAdd() {
-        Runnable runnable = mock(Runnable.class);
-
-        mMultiplexer.addListener(0, runnable, Runnable::run);
-        assertThat(mMultiplexer.mRegistered).isTrue();
-        assertThat(mMultiplexer.mMergedRequest).isEqualTo(0);
-
-        mMultiplexer.notifyListeners();
-        verify(runnable, times(1)).run();
-    }
-
-    @Test
-    public void testAdd_Multiple() {
-        Runnable runnable1 = mock(Runnable.class);
-        Runnable runnable2 = mock(Runnable.class);
-
-        mMultiplexer.addListener(0, runnable1, Runnable::run);
-        mMultiplexer.addListener(0, runnable2, Runnable::run);
-
-        mMultiplexer.notifyListeners();
-        verify(runnable1).run();
-        verify(runnable2).run();
-    }
-
-    @Test
-    public void testRemove() {
-        Runnable runnable = mock(Runnable.class);
-
-        mMultiplexer.addListener(0, runnable, Runnable::run);
-        mMultiplexer.removeListener(runnable);
-        assertThat(mMultiplexer.mRegistered).isFalse();
-
-        mMultiplexer.notifyListeners();
-        verify(runnable, never()).run();
-    }
-
-    @Test
-    public void testRemove_Multiple() {
-        Runnable runnable1 = mock(Runnable.class);
-        Runnable runnable2 = mock(Runnable.class);
-
-        mMultiplexer.addListener(0, runnable1, Runnable::run);
-        mMultiplexer.addListener(1, runnable2, Runnable::run);
-        mMultiplexer.removeListener(runnable1);
-
-        mMultiplexer.notifyListeners();
-        verify(runnable1, never()).run();
-        verify(runnable2).run();
-    }
-
-    @Test
-    public void testMergeMultiple() {
-        Runnable runnable1 = mock(Runnable.class);
-        Runnable runnable2 = mock(Runnable.class);
-        Runnable runnable3 = mock(Runnable.class);
-
-        mMultiplexer.addListener(0, runnable1, Runnable::run);
-        mMultiplexer.addListener(1, runnable2, Runnable::run);
-        assertThat(mMultiplexer.mMergedRequest).isEqualTo(1);
-
-        mMultiplexer.notifyListeners();
-        verify(runnable1, times(1)).run();
-        verify(runnable2, times(1)).run();
-        verify(runnable3, times(0)).run();
-
-        mMultiplexer.addListener(0, runnable3, Runnable::run);
-        assertThat(mMultiplexer.mMergedRequest).isEqualTo(1);
-
-        mMultiplexer.notifyListeners();
-        verify(runnable1, times(2)).run();
-        verify(runnable2, times(2)).run();
-        verify(runnable3, times(1)).run();
-
-        mMultiplexer.removeListener(runnable2);
-        assertThat(mMultiplexer.mMergedRequest).isEqualTo(0);
-
-        mMultiplexer.notifyListeners();
-        verify(runnable1, times(3)).run();
-        verify(runnable2, times(2)).run();
-        verify(runnable3, times(2)).run();
-
-        mMultiplexer.removeListener(runnable1);
-        mMultiplexer.removeListener(runnable3);
-        mMultiplexer.notifyListeners();
-        verify(runnable1, times(3)).run();
-        verify(runnable2, times(2)).run();
-        verify(runnable3, times(2)).run();
-    }
-
-    @Test(timeout = 5000)
-    public void testReentrancy() {
-        AtomicReference<Runnable> runnable = new AtomicReference<>();
-        runnable.set(() -> mMultiplexer.removeListener(runnable.get()));
-
-        mMultiplexer.addListener(0, runnable.get(), command -> {
-            CountDownLatch latch = new CountDownLatch(1);
-            new Handler(Looper.getMainLooper()).post(() -> {
-                command.run();
-                latch.countDown();
-            });
-            try {
-                latch.await();
-            } catch (InterruptedException e) {
-                throw new AssertionError(e);
-            }
-        });
-
-        mMultiplexer.notifyListeners();
-        assertThat(mMultiplexer.mRegistered).isFalse();
-    }
-
-    private static class TestMultiplexer extends ListenerTransportMultiplexer<Integer, Runnable> {
-
-        boolean mRegistered;
-        int mMergedRequest;
-
-        TestMultiplexer() {
-        }
-
-        public void notifyListeners() {
-            deliverToListeners(Runnable::run);
-        }
-
-        @Override
-        protected void registerWithServer(Integer mergedRequest) {
-            mRegistered = true;
-            mMergedRequest = mergedRequest;
-        }
-
-        @Override
-        protected void unregisterWithServer() {
-            mRegistered = false;
-        }
-
-        @Override
-        protected Integer mergeRequests(Collection<Integer> requests) {
-            return requests.stream().max(Comparator.naturalOrder()).get();
-        }
-    }
-}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 59534e4..2c71287 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -114,7 +114,7 @@
     }
 
     void apply(PowerCalculator calculator) {
-        BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0, false);
+        BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0);
         SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats();
         for (int i = 0; i < uidStats.size(); i++) {
             builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 7b1ca8f..018a810 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -66,7 +66,7 @@
         final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
         final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000);
 
-        final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1, true);
+        final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1);
         builder.setConsumedPower(100);
         builder.setDischargePercentage(20);
 
@@ -77,9 +77,6 @@
         uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
         uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500);
-        uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
-                BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
-                        + BatteryConsumer.POWER_COMPONENT_CPU, 510);
         uidBatteryConsumerBuilder.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, 600);
         uidBatteryConsumerBuilder.setUsageDurationMillis(
                 BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700);
@@ -92,9 +89,6 @@
         systemBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100);
         systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
-        systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
-                BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
-                        + BatteryConsumer.POWER_COMPONENT_CPU, 10210);
         systemBatteryConsumerBuilder.setUsageDurationMillis(
                 BatteryConsumer.TIME_COMPONENT_CPU, 10300);
         systemBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis(
@@ -118,9 +112,6 @@
                         BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(400);
                 assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(500);
-                assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
-                        BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
-                                + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(510);
                 assertThat(uidBatteryConsumer.getUsageDurationMillis(
                         BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(600);
                 assertThat(uidBatteryConsumer.getUsageDurationMillis(
@@ -141,9 +132,6 @@
                         BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100);
                 assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10200);
-                assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent(
-                        BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
-                                + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10210);
                 assertThat(systemBatteryConsumer.getUsageDurationMillis(
                         BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(10300);
                 assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis(
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index bf74c8d..1687a78 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -47,9 +47,10 @@
 
         setExternalStatsSyncLocked(new DummyExternalStatsSync());
 
-        final boolean[] supportedBuckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
-        Arrays.fill(supportedBuckets, true);
-        mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedBuckets);
+        final boolean[] supportedStandardBuckets
+                = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS];
+        Arrays.fill(supportedStandardBuckets, true);
+        mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedStandardBuckets, 2);
 
         // A no-op handler.
         mHandler = new Handler(Looper.getMainLooper()) {
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index c9c81ac..b9908f4 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -21,10 +21,11 @@
 import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE;
 import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON;
 import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER;
-import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS;
+import static com.android.internal.power.MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
@@ -47,60 +48,77 @@
 
     @Test
     public void testConstruction() {
-        final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        final int numCustomBuckets = 2;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
+        final MeasuredEnergyStats stats
+                = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
 
-        for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
-            if (supportedEnergyBuckets[i]) {
-                assertTrue(stats.isEnergyBucketSupported(i));
-                assertEquals(0L, stats.getAccumulatedBucketEnergy(i));
+        for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+            if (supportedStandardBuckets[i]) {
+                assertTrue(stats.isStandardBucketSupported(i));
+                assertEquals(0L, stats.getAccumulatedStandardBucketEnergy(i));
             } else {
-                assertFalse(stats.isEnergyBucketSupported(i));
-                assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedBucketEnergy(i));
+                assertFalse(stats.isStandardBucketSupported(i));
+                assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketEnergy(i));
             }
         }
+        for (int i = 0; i < numCustomBuckets; i++) {
+            assertEquals(0L, stats.getAccumulatedCustomBucketEnergy(i));
+        }
     }
 
     @Test
     public void testCreateFromTemplate() {
-        final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        final int numCustomBuckets = 2;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        final MeasuredEnergyStats stats
+                = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        stats.updateCustomBucket(0, 50, true);
+        stats.updateCustomBucket(1, 60, true);
 
         final MeasuredEnergyStats newStats = MeasuredEnergyStats.createFromTemplate(stats);
 
-        for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
-            if (supportedEnergyBuckets[i]) {
-                assertTrue(newStats.isEnergyBucketSupported(i));
-                assertEquals(0, newStats.getAccumulatedBucketEnergy(i));
+        for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+            if (supportedStandardBuckets[i]) {
+                assertTrue(newStats.isStandardBucketSupported(i));
+                assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
             } else {
-                assertFalse(newStats.isEnergyBucketSupported(i));
-                assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
+                assertFalse(newStats.isStandardBucketSupported(i));
+                assertEquals(ENERGY_DATA_UNAVAILABLE,
+                        newStats.getAccumulatedStandardBucketEnergy(i));
             }
         }
+        for (int i = 0; i < numCustomBuckets; i++) {
+            assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(i));
+        }
     }
 
     @Test
     public void testReadWriteParcel() {
-        final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        final int numCustomBuckets = 2;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        final MeasuredEnergyStats stats
+                = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        stats.updateCustomBucket(0, 50, true);
+        stats.updateCustomBucket(1, 60, true);
 
         final Parcel parcel = Parcel.obtain();
         stats.writeToParcel(parcel);
@@ -108,94 +126,127 @@
         parcel.setDataPosition(0);
         MeasuredEnergyStats newStats = new MeasuredEnergyStats(parcel);
 
-        for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
-            assertEquals(stats.getAccumulatedBucketEnergy(i),
-                    newStats.getAccumulatedBucketEnergy(i));
+        for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+            assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+                    newStats.getAccumulatedStandardBucketEnergy(i));
         }
+        for (int i = 0; i < numCustomBuckets; i++) {
+            assertEquals(stats.getAccumulatedCustomBucketEnergy(i),
+                    newStats.getAccumulatedCustomBucketEnergy(i));
+        }
+        assertEquals(ENERGY_DATA_UNAVAILABLE,
+                newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1));
         parcel.recycle();
     }
 
     @Test
     public void testCreateAndReadSummaryFromParcel() {
-        final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        final int numCustomBuckets = 2;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        final MeasuredEnergyStats stats
+                = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        stats.updateCustomBucket(0, 50, true);
+        stats.updateCustomBucket(1, 60, true);
 
         final Parcel parcel = Parcel.obtain();
         MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
         parcel.setDataPosition(0);
         MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel);
 
-        for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
-            assertEquals(stats.isEnergyBucketSupported(i),
-                    newStats.isEnergyBucketSupported(i));
-            assertEquals(stats.getAccumulatedBucketEnergy(i),
-                    newStats.getAccumulatedBucketEnergy(i));
+        for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+            assertEquals(stats.isStandardBucketSupported(i),
+                    newStats.isStandardBucketSupported(i));
+            assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+                    newStats.getAccumulatedStandardBucketEnergy(i));
         }
+        for (int i = 0; i < numCustomBuckets; i++) {
+            assertEquals(stats.getAccumulatedCustomBucketEnergy(i),
+                    newStats.getAccumulatedCustomBucketEnergy(i));
+        }
+        assertEquals(ENERGY_DATA_UNAVAILABLE,
+                newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1));
         parcel.recycle();
     }
 
     @Test
     public void testCreateAndReadSummaryFromParcel_existingTemplate() {
-        final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        final int numCustomBuckets = 2;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats template = new MeasuredEnergyStats(supportedEnergyBuckets);
-        template.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        template.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        template.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        final MeasuredEnergyStats template
+                = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        template.updateCustomBucket(0, 50, true);
 
         final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 7, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true);
+        stats.updateCustomBucket(0, 315, true);
+        stats.updateCustomBucket(1, 316, true);
 
         final Parcel parcel = Parcel.obtain();
         MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
 
-        final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
-        newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
-        newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched from false to true
-        newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true to false
-        final MeasuredEnergyStats newTemplate = new MeasuredEnergyStats(newSupportedEnergyBuckets);
+        final boolean[] newsupportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+        newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched false > true
+        newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true > false
+        final MeasuredEnergyStats newTemplate
+                = new MeasuredEnergyStats(newsupportedStandardBuckets, numCustomBuckets);
         parcel.setDataPosition(0);
 
         final MeasuredEnergyStats newStats =
                 MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate);
 
-        for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
-            if (!newSupportedEnergyBuckets[i]) {
-                assertFalse(newStats.isEnergyBucketSupported(i));
-                assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
-            } else if (!supportedEnergyBuckets[i]) {
-                assertTrue(newStats.isEnergyBucketSupported(i));
-                assertEquals(0L, newStats.getAccumulatedBucketEnergy(i));
+        for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+            if (!newsupportedStandardBuckets[i]) {
+                assertFalse(newStats.isStandardBucketSupported(i));
+                assertEquals(ENERGY_DATA_UNAVAILABLE,
+                        newStats.getAccumulatedStandardBucketEnergy(i));
+            } else if (!supportedStandardBuckets[i]) {
+                assertTrue(newStats.isStandardBucketSupported(i));
+                assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
             } else {
-                assertTrue(newStats.isEnergyBucketSupported(i));
-                assertEquals(stats.getAccumulatedBucketEnergy(i),
-                        newStats.getAccumulatedBucketEnergy(i));
+                assertTrue(newStats.isStandardBucketSupported(i));
+                assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+                        newStats.getAccumulatedStandardBucketEnergy(i));
             }
         }
+        for (int i = 0; i < numCustomBuckets; i++) {
+            assertEquals(stats.getAccumulatedCustomBucketEnergy(i),
+                    newStats.getAccumulatedCustomBucketEnergy(i));
+        }
+        assertEquals(ENERGY_DATA_UNAVAILABLE,
+                newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1));
         parcel.recycle();
     }
 
     @Test
     public void testCreateAndReadSummaryFromParcel_skipZero() {
-        final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
-        Arrays.fill(supportedEnergyBuckets, true);
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        final int numCustomBuckets = 2;
+        Arrays.fill(supportedStandardBuckets, true);
 
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
-        // Accumulate energy in one bucket, the rest should be zero
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
+        final MeasuredEnergyStats stats
+                = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+        // Accumulate energy in one bucket and one custom bucket, the rest should be zero
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
+        stats.updateCustomBucket(1, 60, true);
 
+        // Let's try parcelling with including zeros
         final Parcel includeZerosParcel = Parcel.obtain();
         MeasuredEnergyStats.writeSummaryToParcel(stats, includeZerosParcel, false);
         includeZerosParcel.setDataPosition(0);
@@ -203,90 +254,180 @@
         MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(
                 includeZerosParcel);
 
-        for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+        for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
             if (i == ENERGY_BUCKET_SCREEN_ON) {
-                assertEquals(stats.isEnergyBucketSupported(i),
-                        newStats.isEnergyBucketSupported(i));
-                assertEquals(stats.getAccumulatedBucketEnergy(i),
-                        newStats.getAccumulatedBucketEnergy(i));
+                assertEquals(stats.isStandardBucketSupported(i),
+                        newStats.isStandardBucketSupported(i));
+                assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+                        newStats.getAccumulatedStandardBucketEnergy(i));
             } else {
-                assertTrue(newStats.isEnergyBucketSupported(i));
-                assertEquals(0L, newStats.getAccumulatedBucketEnergy(i));
+                assertTrue(newStats.isStandardBucketSupported(i));
+                assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
             }
         }
+        assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(0));
+        assertEquals(stats.getAccumulatedCustomBucketEnergy(1),
+                newStats.getAccumulatedCustomBucketEnergy(1));
         includeZerosParcel.recycle();
 
+        // Now let's try parcelling with skipping zeros
         final Parcel skipZerosParcel = Parcel.obtain();
         MeasuredEnergyStats.writeSummaryToParcel(stats, skipZerosParcel, true);
         skipZerosParcel.setDataPosition(0);
 
         newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(skipZerosParcel);
 
-        for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+        for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
             if (i == ENERGY_BUCKET_SCREEN_ON) {
-                assertEquals(stats.isEnergyBucketSupported(i),
-                        newStats.isEnergyBucketSupported(i));
-                assertEquals(stats.getAccumulatedBucketEnergy(i),
-                        newStats.getAccumulatedBucketEnergy(i));
+                assertEquals(stats.isStandardBucketSupported(i),
+                        newStats.isStandardBucketSupported(i));
+                assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+                        newStats.getAccumulatedStandardBucketEnergy(i));
             } else {
-                assertFalse(newStats.isEnergyBucketSupported(i));
-                assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
+                assertFalse(newStats.isStandardBucketSupported(i));
+                assertEquals(ENERGY_DATA_UNAVAILABLE,
+                        newStats.getAccumulatedStandardBucketEnergy(i));
             }
         }
+        assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(0));
+        assertEquals(stats.getAccumulatedCustomBucketEnergy(1),
+                newStats.getAccumulatedCustomBucketEnergy(1));
         skipZerosParcel.recycle();
     }
 
     @Test
+    public void testCreateAndReadSummaryFromParcel_nullTemplate() {
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        final int numCustomBuckets = 2;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+
+        final MeasuredEnergyStats stats
+                = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        stats.updateCustomBucket(0, 50, true);
+        stats.updateCustomBucket(1, 60, true);
+
+        final Parcel parcel = Parcel.obtain();
+        MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
+        parcel.setDataPosition(0);
+
+        MeasuredEnergyStats newStats
+                = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, null);
+        assertNull(newStats);
+        parcel.recycle();
+    }
+
+    @Test
+    public void testCreateAndReadSummaryFromParcel_boring() {
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        final int numCustomBuckets = 2;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+
+        final MeasuredEnergyStats template
+                = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        template.updateCustomBucket(0, 50, true);
+
+        final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L, true);
+
+        final Parcel parcel = Parcel.obtain();
+        MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
+
+        final boolean[] newSupportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+        newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched false > true
+        newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true > false
+        final MeasuredEnergyStats newTemplate
+                = new MeasuredEnergyStats(newSupportedStandardBuckets, numCustomBuckets);
+        parcel.setDataPosition(0);
+
+        final MeasuredEnergyStats newStats =
+                MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate);
+        // The only non-0 entry in stats is no longer supported, so now there's no interesting data.
+        assertNull(newStats);
+        assertEquals("Parcel was not properly consumed", 0, parcel.dataAvail());
+        parcel.recycle();
+    }
+
+    @Test
     public void testUpdateBucket() {
-        final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        final int numCustomBuckets = 2;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+        final MeasuredEnergyStats stats
+                = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
 
-        assertEquals(15, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
+        stats.updateCustomBucket(0, 50, true);
+        stats.updateCustomBucket(1, 60, true);
+        stats.updateCustomBucket(0, 3, true);
+
+        assertEquals(15, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
         assertEquals(ENERGY_DATA_UNAVAILABLE,
-                stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE));
-        assertEquals(40, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_OTHER));
+                stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE));
+        assertEquals(40, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_OTHER));
+        assertEquals(50 + 3, stats.getAccumulatedCustomBucketEnergy(0));
+        assertEquals(60, stats.getAccumulatedCustomBucketEnergy(1));
     }
 
     @Test
     public void testReset() {
-        final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
-        supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+        final int numCustomBuckets = 2;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+        supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        final MeasuredEnergyStats stats
+                = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+        stats.updateCustomBucket(0, 50, true);
+        stats.updateCustomBucket(1, 60, true);
 
         MeasuredEnergyStats.resetIfNotNull(stats);
         // All energy should be reset to 0
-        for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
-            if (supportedEnergyBuckets[i]) {
-                assertTrue(stats.isEnergyBucketSupported(i));
-                assertEquals(0, stats.getAccumulatedBucketEnergy(i));
+        for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+            if (supportedStandardBuckets[i]) {
+                assertTrue(stats.isStandardBucketSupported(i));
+                assertEquals(0, stats.getAccumulatedStandardBucketEnergy(i));
             } else {
-                assertFalse(stats.isEnergyBucketSupported(i));
-                assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedBucketEnergy(i));
+                assertFalse(stats.isStandardBucketSupported(i));
+                assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketEnergy(i));
             }
         }
+        for (int i = 0; i < numCustomBuckets; i++) {
+            assertEquals(0, stats.getAccumulatedCustomBucketEnergy(i));
+        }
 
         // Values should increase as usual.
-        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 70, true);
-        assertEquals(70L, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70, true);
+        assertEquals(70L, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
+
+        stats.updateCustomBucket(1, 12, true);
+        assertEquals(12L, stats.getAccumulatedCustomBucketEnergy(1));
     }
 
     /** Test that states are mapped to the expected energy buckets. Beware of mapping changes. */
     @Test
-    public void testEnergyBucketMapping() {
+    public void testStandardBucketMapping() {
         int exp;
 
         exp = ENERGY_BUCKET_SCREEN_ON;
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
deleted file mode 100644
index 5a3ea35..0000000
--- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * Copyright (C) 2021 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.Display.DEFAULT_DISPLAY;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_90;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManagerGlobal;
-import android.util.DisplayMetrics;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.mockito.quality.Strictness;
-
-import java.util.function.Consumer;
-
-/**
- * Tests for {@link Display}.
- *
- * <p>Build/Install/Run:
- *
- * atest FrameworksMockingCoreTests:android.view.DisplayTests
- */
-@RunWith(AndroidJUnit4.class)
-public class DisplayTests {
-
-    private static final int APP_WIDTH = 272;
-    private static final int APP_HEIGHT = 700;
-    // Tablet size device, ROTATION_0 corresponds to portrait.
-    private static final int LOGICAL_WIDTH = 700;
-    private static final int LOGICAL_HEIGHT = 1800;
-
-    // Bounds of the app when the device is in portrait mode.
-    private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
-    private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
-
-    private StaticMockitoSession mMockitoSession;
-
-    private DisplayManagerGlobal mDisplayManagerGlobal;
-    private Context mApplicationContext;
-    private DisplayInfo mDisplayInfo = new DisplayInfo();
-
-    @Before
-    public void setupTests() {
-        mMockitoSession = mockitoSession()
-                .mockStatic(DisplayManagerGlobal.class)
-                .strictness(Strictness.LENIENT)
-                .startMocking();
-
-        // Ensure no adjustments are set before each test.
-        mApplicationContext = ApplicationProvider.getApplicationContext();
-        DisplayAdjustments displayAdjustments =
-                mApplicationContext.getResources().getDisplayAdjustments();
-        displayAdjustments.setFixedRotationAdjustments(null);
-        mApplicationContext.getResources().overrideDisplayAdjustments(null);
-        mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
-                null);
-        mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
-                null);
-        mDisplayInfo.rotation = ROTATION_0;
-
-        mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
-        doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
-    }
-
-    @After
-    public void teardownTests() {
-        if (mMockitoSession != null) {
-            mMockitoSession.finishMocking();
-        }
-        Mockito.framework().clearInlineMocks();
-    }
-
-    @Test
-    public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        assertThat(display.getDisplayAdjustments()).isEqualTo(
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        DisplayInfo actualDisplayInfo = new DisplayInfo();
-        display.getDisplayInfo(actualDisplayInfo);
-        verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
-    }
-
-    @Test
-    public void testConstructor_defaultResources_matchesDisplayInfo() {
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        assertThat(display.getDisplayAdjustments()).isEqualTo(
-                mApplicationContext.getResources().getDisplayAdjustments());
-        DisplayInfo actualDisplayInfo = new DisplayInfo();
-        display.getDisplayInfo(actualDisplayInfo);
-        verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
-    }
-
-    @Test
-    public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        assertThat(display.getRotation()).isEqualTo(ROTATION_0);
-    }
-
-    @Test
-    public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, but no override is set.
-        DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-        final FixedRotationAdjustments fixedRotationAdjustments =
-                new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
-                        DisplayCutout.NO_CUTOUT);
-        displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
-        // GIVEN display is constructed with display adjustments.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                displayAdjustments);
-        // THEN rotation is not adjusted since no override was set.
-        assertThat(display.getRotation()).isEqualTo(ROTATION_0);
-    }
-
-    @Test
-    public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, but no override is set.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN rotation is not adjusted since no override is set.
-        assertThat(display.getRotation()).isEqualTo(ROTATION_0);
-    }
-
-    @Test
-    public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN rotation is adjusted since an override is set.
-        assertThat(display.getRotation()).isEqualTo(ROTATION_90);
-    }
-
-    @Test
-    public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real size matches display orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real size matches display orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsPortrait);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app bounds.
-        verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsLandscape);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app bounds.
-        verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated with an override.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsPortrait);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app bounds.
-        verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsLandscape);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app bounds.
-        verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
-    }
-
-    // Given rotated display dimensions, calculate the letterboxed app bounds.
-    private static Rect buildAppBounds(int displayWidth, int displayHeight) {
-        final int midWidth = displayWidth / 2;
-        final int left = midWidth - (APP_WIDTH / 2);
-        final int right = midWidth + (APP_WIDTH / 2);
-        final int midHeight = displayHeight / 2;
-        // Coordinate system starts at top left.
-        final int top = midHeight - (APP_HEIGHT / 2);
-        final int bottom = midHeight + (APP_HEIGHT / 2);
-        return new Rect(left, top, right, bottom);
-    }
-
-    private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
-        displayInfo.rotation = ROTATION_90;
-        // Flip width & height assignment since the device is rotated.
-        displayInfo.logicalWidth = LOGICAL_HEIGHT;
-        displayInfo.logicalHeight = LOGICAL_WIDTH;
-    }
-
-    private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
-        displayInfo.rotation = ROTATION_0;
-        displayInfo.logicalWidth = LOGICAL_WIDTH;
-        displayInfo.logicalHeight = LOGICAL_HEIGHT;
-    }
-
-    /**
-     * Set max bounds to be sandboxed to the app bounds, indicating the app is in
-     * size compat mode or letterbox.
-     */
-    private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
-        resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
-    }
-
-    /**
-     * Do not compare entire display info, since it is updated to match display the test is run on.
-     */
-    private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
-        assertThat(actual.displayId).isEqualTo(expected.displayId);
-        assertThat(actual.rotation).isEqualTo(expected.rotation);
-        assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
-        assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
-    }
-
-    private static void verifyRealSizeIsLandscape(Display display) {
-        Point size = new Point();
-        display.getRealSize(size);
-        // Flip the width and height check since the device is rotated.
-        assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
-    }
-
-    private static void verifyRealMetricsIsLandscape(Display display) {
-        DisplayMetrics metrics = new DisplayMetrics();
-        display.getRealMetrics(metrics);
-        // Flip the width and height check since the device is rotated.
-        assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
-        assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
-    }
-
-    private static void verifyRealSizeIsPortrait(Display display) {
-        Point size = new Point();
-        display.getRealSize(size);
-        assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
-    }
-
-    private static void verifyRealMetricsIsPortrait(Display display) {
-        DisplayMetrics metrics = new DisplayMetrics();
-        display.getRealMetrics(metrics);
-        assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
-        assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
-    }
-
-    private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
-        Point size = new Point();
-        display.getRealSize(size);
-        assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
-    }
-
-    private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
-        DisplayMetrics metrics = new DisplayMetrics();
-        display.getRealMetrics(metrics);
-        assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
-        assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
-    }
-
-    private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
-            Resources resources, @Surface.Rotation int rotation) {
-        FixedRotationAdjustments fixedRotationAdjustments =
-                setFixedRotationAdjustments(resources, rotation);
-        resources.overrideDisplayAdjustments(
-                buildOverrideRotationAdjustments(fixedRotationAdjustments));
-        return fixedRotationAdjustments;
-    }
-
-    private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
-            @Surface.Rotation int rotation) {
-        final FixedRotationAdjustments fixedRotationAdjustments =
-                new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
-                        DisplayCutout.NO_CUTOUT);
-        resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
-        return fixedRotationAdjustments;
-    }
-
-    private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
-            FixedRotationAdjustments fixedRotationAdjustments) {
-        return consumedDisplayAdjustments
-                -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
-    }
-}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index ae8e3ce..cdc61a1 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -468,6 +468,7 @@
         <!-- Permission required for CTS test - CallLogTest -->
         <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
         <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
+        <permission name="android.permission.MODIFY_QUIET_MODE" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ac8a296..222c9bd 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1903,6 +1903,12 @@
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "123161180": {
+      "message": "SEVER CHILDREN",
+      "level": "INFO",
+      "group": "WM_SHOW_TRANSACTIONS",
+      "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+    },
     "140319294": {
       "message": "IME target changed within ActivityRecord",
       "level": "DEBUG",
@@ -2137,12 +2143,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "332390227": {
-      "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing? %s mismatch with parent bounds? %s size compat mode %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_CONFIGURATION",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "342460966": {
       "message": "DRAG %s: pos=(%d,%d)",
       "level": "INFO",
@@ -2623,12 +2623,6 @@
       "group": "WM_DEBUG_WINDOW_ORGANIZER",
       "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
     },
-    "910200295": {
-      "message": "Sandbox max bounds due to mismatched orientation with parent, to %s vs DisplayArea %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_CONFIGURATION",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "913494177": {
       "message": "removeAllWindowsIfPossible: removing win=%s",
       "level": "WARN",
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index a7d3f798..5b79d76 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -175,6 +175,39 @@
     public static final int Y16 = 0x20363159;
 
     /**
+     * <p>Android YUV P010 format.</p>
+     *
+     * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane
+     * followed immediately by a Wx(H/2) CbCr plane. Each sample is
+     * represented by a 16-bit little-endian value, with the lower 6 bits set
+     * to zero.
+     *
+     * <p>This format assumes
+     * <ul>
+     * <li>an even height</li>
+     * <li>a vertical stride equal to the height</li>
+     * </ul>
+     * </p>
+     *
+     * <pre>   stride_in_bytes = stride * 2 </pre>
+     * <pre>   y_size = stride_in_bytes * height </pre>
+     * <pre>   cbcr_size = stride_in_bytes * (height / 2) </pre>
+     * <pre>   cb_offset = y_size </pre>
+     * <pre>   cr_offset = cb_offset + 2 </pre>
+     *
+     * <p>For example, the {@link android.media.Image} object can provide data
+     * in this format from a {@link android.hardware.camera2.CameraDevice}
+     * through a {@link android.media.ImageReader} object if this format is
+     * supported by {@link android.hardware.camera2.CameraDevice}.</p>
+     *
+     * @see android.media.Image
+     * @see android.media.ImageReader
+     * @see android.hardware.camera2.CameraDevice
+     *
+     */
+    public static final int YCBCR_P010 = 0x36;
+
+    /**
      * YCbCr format, used for video.
      *
      * <p>For the {@link android.hardware.camera2} API, the {@link #YUV_420_888} format is
@@ -807,6 +840,8 @@
             case RAW_DEPTH:
             case RAW_SENSOR:
                 return 16;
+            case YCBCR_P010:
+                return 20;
             case RAW_DEPTH10:
             case RAW10:
                 return 10;
@@ -839,6 +874,7 @@
             case YUV_420_888:
             case YUV_422_888:
             case YUV_444_888:
+            case YCBCR_P010:
             case FLEX_RGB_888:
             case FLEX_RGBA_8888:
             case RAW_SENSOR:
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 005a726..35e6b859 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -42,6 +42,7 @@
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.text.FontConfig;
+import android.util.ArrayMap;
 import android.util.Base64;
 import android.util.LongSparseArray;
 import android.util.LruCache;
@@ -67,7 +68,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -147,7 +147,7 @@
      */
     @GuardedBy("SYSTEM_FONT_MAP_LOCK")
     @UnsupportedAppUsage(trackingBug = 123769347)
-    static final Map<String, Typeface> sSystemFontMap = new HashMap<>();
+    static final Map<String, Typeface> sSystemFontMap = new ArrayMap<>();
 
     // DirectByteBuffer object to hold sSystemFontMap's backing memory mapping.
     static ByteBuffer sSystemFontMapBuffer = null;
@@ -1231,7 +1231,7 @@
     /** @hide */
     @VisibleForTesting
     public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException {
-        Map<String, Typeface> fontMap = new HashMap<>();
+        Map<String, Typeface> fontMap = new ArrayMap<>();
         int typefacesBytesCount = buffer.getInt();
         long[] nativePtrs = nativeReadTypefaces(buffer.slice());
         if (nativePtrs == null) {
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index c29c194..77f86fe 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -20,15 +20,16 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.text.FontConfig;
+import android.util.SparseIntArray;
 
 import com.android.internal.util.Preconditions;
 
 import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
 
 import libcore.util.NativeAllocationRegistry;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 
 /**
  * A font family class can be used for creating Typeface.
@@ -68,7 +69,9 @@
                     nGetReleaseNativeFamily());
 
         private final ArrayList<Font> mFonts = new ArrayList<>();
-        private final HashSet<Integer> mStyleHashSet = new HashSet<>();
+        // Most FontFamily only has  regular, bold, italic, bold-italic. Thus 4 should be good for
+        // initial capacity.
+        private final SparseIntArray mStyles = new SparseIntArray(4);
 
         /**
          * Constructs a builder.
@@ -77,7 +80,7 @@
          */
         public Builder(@NonNull Font font) {
             Preconditions.checkNotNull(font, "font can not be null");
-            mStyleHashSet.add(makeStyleIdentifier(font));
+            mStyles.append(makeStyleIdentifier(font), 0);
             mFonts.add(font);
         }
 
@@ -97,9 +100,11 @@
          */
         public @NonNull Builder addFont(@NonNull Font font) {
             Preconditions.checkNotNull(font, "font can not be null");
-            if (!mStyleHashSet.add(makeStyleIdentifier(font))) {
+            int key = makeStyleIdentifier(font);
+            if (mStyles.indexOfKey(key) >= 0) {
                 throw new IllegalArgumentException(font + " has already been added");
             }
+            mStyles.append(key, 0);
             mFonts.add(font);
             return this;
         }
@@ -120,7 +125,7 @@
                 nAddFont(builderPtr, mFonts.get(i).getNativePtr());
             }
             final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback);
-            final FontFamily family = new FontFamily(mFonts, langTags, variant, ptr);
+            final FontFamily family = new FontFamily(mFonts, ptr);
             sFamilyRegistory.registerNativeAllocation(family, ptr);
             return family;
         }
@@ -139,15 +144,11 @@
     }
 
     private final ArrayList<Font> mFonts;
-    private final String mLangTags;
-    private final int mVariant;
     private final long mNativePtr;
 
     // Use Builder instead.
-    private FontFamily(@NonNull ArrayList<Font> fonts, String langTags, int variant, long ptr) {
+    private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
         mFonts = fonts;
-        mLangTags = langTags;
-        mVariant = variant;
         mNativePtr = ptr;
     }
 
@@ -157,7 +158,7 @@
      * @return a BCP-47 compliant language tag.
      */
     public @Nullable String getLangTags() {
-        return mLangTags;
+        return nGetLangTags(mNativePtr);
     }
 
     /**
@@ -165,7 +166,7 @@
      * @return a family variant
      */
     public int getVariant() {
-        return mVariant;
+        return nGetVariant(mNativePtr);
     }
 
     /**
@@ -191,4 +192,10 @@
     public long getNativePtr() {
         return mNativePtr;
     }
+
+    @FastNative
+    private static native String nGetLangTags(long family);
+
+    @CriticalNative
+    private static native int nGetVariant(long family);
 }
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index c166e12..904085f 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -22,6 +22,7 @@
 import android.graphics.Typeface;
 import android.text.FontConfig;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -36,8 +37,6 @@
 import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -76,7 +75,7 @@
             if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
                 sAvailableFonts = collectAllFonts();
             } else {
-                Set<Font> set = new HashSet<>();
+                Set<Font> set = new ArraySet<>();
                 for (FontFamily[] items : sFamilyMap.values()) {
                     for (FontFamily family : items) {
                         for (int i = 0; i < family.getSize(); ++i) {
@@ -96,7 +95,7 @@
         FontConfig fontConfig = getSystemPreinstalledFontConfig();
         Map<String, FontFamily[]> map = buildSystemFallback(fontConfig);
 
-        Set<Font> res = new HashSet<>();
+        Set<Font> res = new ArraySet<>();
         for (FontFamily[] families : map.values()) {
             for (FontFamily family : families) {
                 for (int i = 0; i < family.getSize(); ++i) {
@@ -218,7 +217,7 @@
     }
 
     private static void appendNamedFamily(@NonNull FontConfig.FontFamily xmlFamily,
-            @NonNull HashMap<String, ByteBuffer> bufferCache,
+            @NonNull ArrayMap<String, ByteBuffer> bufferCache,
             @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) {
         final String familyName = xmlFamily.getName();
         final FontFamily family = createFontFamily(
@@ -284,8 +283,8 @@
      */
     @VisibleForTesting
     public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) {
-        final Map<String, FontFamily[]> fallbackMap = new HashMap<>();
-        final HashMap<String, ByteBuffer> bufferCache = new HashMap<>();
+        final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>();
         final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();
 
         final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
@@ -326,7 +325,7 @@
     public static Map<String, Typeface> buildSystemTypefaces(
             FontConfig fontConfig,
             Map<String, FontFamily[]> fallbackMap) {
-        final HashMap<String, Typeface> result = new HashMap<>();
+        final ArrayMap<String, Typeface> result = new ArrayMap<>();
         Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result);
         return result;
     }
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index b1b6306..16bf546 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -23,7 +23,6 @@
 import android.security.keymaster.ExportResult;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterDefs;
-import android.sysprop.Keystore2Properties;
 
 import java.io.IOException;
 import java.security.KeyFactory;
@@ -117,8 +116,6 @@
         putSecretKeyFactoryImpl("HmacSHA512");
     }
 
-    private static boolean sKeystore2Enabled;
-
     /**
      * This function indicates whether or not Keystore 2.0 is enabled. Some parts of the
      * Keystore SPI must behave subtly differently when Keystore 2.0 is enabled. However,
@@ -133,10 +130,9 @@
      * @hide
      */
     public static boolean isKeystore2Enabled() {
-        return sKeystore2Enabled;
+        return android.security.keystore2.AndroidKeyStoreProvider.isInstalled();
     }
 
-
     /**
      * Installs a new instance of this provider (and the
      * {@link AndroidKeyStoreBCWorkaroundProvider}).
@@ -164,11 +160,6 @@
             // priority.
             Security.addProvider(workaroundProvider);
         }
-
-        // {@code install()} is run by zygote when this property is still accessible. We store its
-        // value so that the Keystore SPI can act accordingly without having to access an internal
-        // property.
-        sKeystore2Enabled = Keystore2Properties.keystore2_enabled().orElse(false);
     }
 
     private void putSecretKeyFactoryImpl(String algorithm) {
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index 7fbbb61..4612ba2 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Sidecar
 android_library_import {
     name: "window-sidecar",
     aars: ["window-sidecar-release.aar"],
@@ -20,7 +21,7 @@
 
 java_library {
     name: "androidx.window.sidecar",
-    srcs: ["src/**/*.java"],
+    srcs: ["src/androidx/window/sidecar/**/*.java", "src/androidx/window/util/**/*.java"],
     static_libs: ["window-sidecar"],
     installable: true,
     sdk_version: "core_platform",
@@ -36,3 +37,31 @@
     src: "androidx.window.sidecar.xml",
     filename_from_src: true,
 }
+
+// Extensions
+// NOTE: This module is still under active development and must not
+// be used in production. Use 'androidx.window.sidecar' instead.
+android_library_import {
+    name: "window-extensions",
+    aars: ["window-extensions-release.aar"],
+    sdk_version: "current",
+}
+
+java_library {
+    name: "androidx.window.extensions",
+    srcs: ["src/androidx/window/extensions/**/*.java", "src/androidx/window/util/**/*.java"],
+    static_libs: ["window-extensions"],
+    installable: true,
+    sdk_version: "core_platform",
+    system_ext_specific: true,
+    libs: ["framework", "androidx.annotation_annotation",],
+    required: ["androidx.window.extensions.xml",],
+}
+
+prebuilt_etc {
+    name: "androidx.window.extensions.xml",
+    system_ext_specific: true,
+    sub_dir: "permissions",
+    src: "androidx.window.extensions.xml",
+    filename_from_src: true,
+}
diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
new file mode 100644
index 0000000..34264aa
--- /dev/null
+++ b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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.
+  -->
+<permissions>
+    <library
+        name="androidx.window.extensions"
+        file="/system_ext/framework/androidx.window.extensions.jar"/>
+</permissions>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
new file mode 100644
index 0000000..b7a6039
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.extensions;
+
+import android.content.Context;
+
+/**
+ * Provider class that will instantiate the library implementation. It must be included in the
+ * vendor library, and the vendor implementation must match the signature of this class.
+ */
+public class ExtensionProvider {
+    /**
+     * Provides a simple implementation of {@link ExtensionInterface} that can be replaced by
+     * an OEM by overriding this method.
+     */
+    public static ExtensionInterface getExtensionImpl(Context context) {
+        return new SampleExtensionImpl(context);
+    }
+
+    /**
+     * The support library will use this method to check API version compatibility.
+     * @return API version string in MAJOR.MINOR.PATCH-description format.
+     */
+    public static String getApiVersion() {
+        return "1.0.0-settings_sample";
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
new file mode 100644
index 0000000..0bf6965
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.extensions;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.window.util.BaseDisplayFeature;
+import androidx.window.util.SettingsConfigProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reference implementation of androidx.window.extensions OEM interface for use with
+ * WindowManager Jetpack.
+ *
+ * NOTE: This version is a work in progress and under active development. It MUST NOT be used in
+ * production builds since the interface can still change before reaching stable version.
+ * Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
+ */
+class SampleExtensionImpl extends StubExtension implements
+        SettingsConfigProvider.StateChangeCallback {
+    private static final String TAG = "SampleExtension";
+
+    private final SettingsConfigProvider mConfigProvider;
+
+    SampleExtensionImpl(Context context) {
+        mConfigProvider = new SettingsConfigProvider(context, this);
+    }
+
+    @Override
+    public void onDevicePostureChanged() {
+        updateDeviceState(new ExtensionDeviceState(mConfigProvider.getDeviceState()));
+    }
+
+    @Override
+    public void onDisplayFeaturesChanged() {
+        for (Activity activity : getActivitiesListeningForLayoutChanges()) {
+            ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
+            updateWindowLayout(activity, newLayout);
+        }
+    }
+
+    @NonNull
+    private ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
+        List<ExtensionDisplayFeature> displayFeatures = getDisplayFeatures(activity);
+        return new ExtensionWindowLayoutInfo(displayFeatures);
+    }
+
+    private List<ExtensionDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+        List<ExtensionDisplayFeature> features = new ArrayList<>();
+        int displayId = activity.getDisplayId();
+        if (displayId != DEFAULT_DISPLAY) {
+            Log.w(TAG, "This sample doesn't support display features on secondary displays");
+            return features;
+        }
+
+        if (activity.isInMultiWindowMode()) {
+            // It is recommended not to report any display features in multi-window mode, since it
+            // won't be possible to synchronize the display feature positions with window movement.
+            return features;
+        }
+
+        List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
+        for (BaseDisplayFeature baseFeature : storedFeatures) {
+            Rect featureRect = baseFeature.getRect();
+            rotateRectToDisplayRotation(displayId, featureRect);
+            transformToWindowSpaceRect(activity, featureRect);
+            features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
+                    baseFeature.getState()));
+        }
+        return features;
+    }
+
+    @Override
+    protected void onListenersChanged() {
+        if (hasListeners()) {
+            mConfigProvider.registerObserversIfNeeded();
+        } else {
+            mConfigProvider.unregisterObserversIfNeeded();
+        }
+
+        onDevicePostureChanged();
+        onDisplayFeaturesChanged();
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
new file mode 100644
index 0000000..b0895ef
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.extensions;
+
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Basic implementation of the {@link ExtensionInterface}. An OEM can choose to use it as the base
+ * class for their implementation.
+ */
+abstract class StubExtension implements ExtensionInterface {
+
+    private ExtensionCallback mExtensionCallback;
+    private final Set<Activity> mWindowLayoutChangeListenerActivities = new HashSet<>();
+    private boolean mDeviceStateChangeListenerRegistered;
+
+    StubExtension() {
+    }
+
+    @Override
+    public void setExtensionCallback(@NonNull ExtensionCallback extensionCallback) {
+        this.mExtensionCallback = extensionCallback;
+    }
+
+    @Override
+    public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
+        this.mWindowLayoutChangeListenerActivities.add(activity);
+        this.onListenersChanged();
+    }
+
+    @Override
+    public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
+        this.mWindowLayoutChangeListenerActivities.remove(activity);
+        this.onListenersChanged();
+    }
+
+    @Override
+    public void onDeviceStateListenersChanged(boolean isEmpty) {
+        this.mDeviceStateChangeListenerRegistered = !isEmpty;
+        this.onListenersChanged();
+    }
+
+    void updateDeviceState(ExtensionDeviceState newState) {
+        if (this.mExtensionCallback != null) {
+            mExtensionCallback.onDeviceStateChanged(newState);
+        }
+    }
+
+    void updateWindowLayout(@NonNull Activity activity,
+            @NonNull ExtensionWindowLayoutInfo newLayout) {
+        if (this.mExtensionCallback != null) {
+            mExtensionCallback.onWindowLayoutChanged(activity, newLayout);
+        }
+    }
+
+    @NonNull
+    Set<Activity> getActivitiesListeningForLayoutChanges() {
+        return mWindowLayoutChangeListenerActivities;
+    }
+
+    protected boolean hasListeners() {
+        return !mWindowLayoutChangeListenerActivities.isEmpty()
+                || mDeviceStateChangeListenerRegistered;
+    }
+
+    protected abstract void onListenersChanged();
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
new file mode 100644
index 0000000..1094a0e
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 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 androidx.window.sidecar;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.window.util.BaseDisplayFeature;
+import androidx.window.util.SettingsConfigProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reference implementation of androidx.window.sidecar OEM interface for use with
+ * WindowManager Jetpack.
+ */
+class SampleSidecarImpl extends StubSidecar implements
+        SettingsConfigProvider.StateChangeCallback {
+    private static final String TAG = "SampleSidecar";
+
+    private final SettingsConfigProvider mConfigProvider;
+
+    SampleSidecarImpl(Context context) {
+        mConfigProvider = new SettingsConfigProvider(context, this);
+    }
+
+    @Override
+    public void onDevicePostureChanged() {
+        updateDeviceState(getDeviceState());
+    }
+
+    @Override
+    public void onDisplayFeaturesChanged() {
+        for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
+            SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
+            updateWindowLayout(windowToken, newLayout);
+        }
+    }
+
+    @NonNull
+    @Override
+    public SidecarDeviceState getDeviceState() {
+        SidecarDeviceState deviceState = new SidecarDeviceState();
+        deviceState.posture = mConfigProvider.getDeviceState();
+        return deviceState;
+    }
+
+    @NonNull
+    @Override
+    public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
+        Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
+        SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
+        if (activity == null) {
+            return windowLayoutInfo;
+        }
+        windowLayoutInfo.displayFeatures = getDisplayFeatures(activity);
+        return windowLayoutInfo;
+    }
+
+    private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+        List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
+        int displayId = activity.getDisplayId();
+        if (displayId != DEFAULT_DISPLAY) {
+            Log.w(TAG, "This sample doesn't support display features on secondary displays");
+            return features;
+        }
+
+        if (activity.isInMultiWindowMode()) {
+            // It is recommended not to report any display features in multi-window mode, since it
+            // won't be possible to synchronize the display feature positions with window movement.
+            return features;
+        }
+
+        List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
+        for (BaseDisplayFeature baseFeature : storedFeatures) {
+            SidecarDisplayFeature feature = new SidecarDisplayFeature();
+            Rect featureRect = baseFeature.getRect();
+            rotateRectToDisplayRotation(displayId, featureRect);
+            transformToWindowSpaceRect(activity, featureRect);
+            feature.setRect(featureRect);
+            feature.setType(baseFeature.getType());
+            features.add(feature);
+        }
+        return features;
+    }
+
+    @Override
+    protected void onListenersChanged() {
+        if (hasListeners()) {
+            mConfigProvider.registerObserversIfNeeded();
+        } else {
+            mConfigProvider.unregisterObserversIfNeeded();
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
deleted file mode 100644
index 5397302..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2020 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 androidx.window.sidecar;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static androidx.window.sidecar.SidecarHelper.getWindowDisplay;
-import static androidx.window.sidecar.SidecarHelper.isInMultiWindow;
-import static androidx.window.sidecar.SidecarHelper.rotateRectToDisplayRotation;
-import static androidx.window.sidecar.SidecarHelper.transformToWindowSpaceRect;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-class SettingsSidecarImpl extends StubSidecar {
-    private static final String TAG = "SettingsSidecar";
-
-    private static final String DEVICE_POSTURE = "device_posture";
-    private static final String DISPLAY_FEATURES = "display_features";
-
-    private static final Pattern FEATURE_PATTERN =
-            Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
-
-    private static final String FEATURE_TYPE_FOLD = "fold";
-    private static final String FEATURE_TYPE_HINGE = "hinge";
-
-    private Context mContext;
-    private SettingsObserver mSettingsObserver;
-
-    final class SettingsObserver extends ContentObserver {
-        private final Uri mDevicePostureUri =
-                Settings.Global.getUriFor(DEVICE_POSTURE);
-        private final Uri mDisplayFeaturesUri =
-                Settings.Global.getUriFor(DISPLAY_FEATURES);
-        private final ContentResolver mResolver = mContext.getContentResolver();
-        private boolean mRegisteredObservers;
-
-
-        private SettingsObserver() {
-            super(new Handler(Looper.getMainLooper()));
-        }
-
-        private void registerObserversIfNeeded() {
-            if (mRegisteredObservers) {
-                return;
-            }
-            mRegisteredObservers = true;
-            mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendents */,
-                    this /* ContentObserver */);
-            mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendents */,
-                    this /* ContentObserver */);
-        }
-
-        private void unregisterObserversIfNeeded() {
-            if (!mRegisteredObservers) {
-                return;
-            }
-            mRegisteredObservers = false;
-            mResolver.unregisterContentObserver(this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            if (uri == null) {
-                return;
-            }
-
-            if (mDevicePostureUri.equals(uri)) {
-                updateDevicePosture();
-                return;
-            }
-            if (mDisplayFeaturesUri.equals(uri)) {
-                updateDisplayFeatures();
-                return;
-            }
-        }
-    }
-
-    SettingsSidecarImpl(Context context) {
-        mContext = context;
-        mSettingsObserver = new SettingsObserver();
-    }
-
-    private void updateDevicePosture() {
-        updateDeviceState(getDeviceState());
-    }
-
-    /** Update display features with values read from settings. */
-    private void updateDisplayFeatures() {
-        for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
-            SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
-            updateWindowLayout(windowToken, newLayout);
-        }
-    }
-
-    @NonNull
-    @Override
-    public SidecarDeviceState getDeviceState() {
-        ContentResolver resolver = mContext.getContentResolver();
-        int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE,
-                SidecarDeviceState.POSTURE_UNKNOWN);
-        SidecarDeviceState deviceState = new SidecarDeviceState();
-        deviceState.posture = posture;
-        return deviceState;
-    }
-
-    @NonNull
-    @Override
-    public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
-        List<SidecarDisplayFeature> displayFeatures = readDisplayFeatures(windowToken);
-        SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
-        windowLayoutInfo.displayFeatures = displayFeatures;
-        return windowLayoutInfo;
-    }
-
-    private List<SidecarDisplayFeature> readDisplayFeatures(IBinder windowToken) {
-        List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
-        int displayId = getWindowDisplay(windowToken);
-        if (displayId != DEFAULT_DISPLAY) {
-            Log.w(TAG, "This sample doesn't support display features on secondary displays");
-            return features;
-        }
-
-        if (isInMultiWindow(windowToken)) {
-            // It is recommended not to report any display features in multi-window mode, since it
-            // won't be possible to synchronize the display feature positions with window movement.
-            return features;
-        }
-
-        ContentResolver resolver = mContext.getContentResolver();
-        String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_FEATURES);
-        if (TextUtils.isEmpty(displayFeaturesString)) {
-            displayFeaturesString = mContext.getResources().getString(
-                    R.string.config_display_features);
-        }
-        if (TextUtils.isEmpty(displayFeaturesString)) {
-            return features;
-        }
-
-        String[] featureStrings = displayFeaturesString.split(";");
-        for (String featureString : featureStrings) {
-            Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString);
-            if (!featureMatcher.matches()) {
-                Log.e(TAG, "Malformed feature description format: " + featureString);
-                continue;
-            }
-            try {
-                String featureType = featureMatcher.group(1);
-                int type;
-                switch (featureType) {
-                    case FEATURE_TYPE_FOLD:
-                        type = SidecarDisplayFeature.TYPE_FOLD;
-                        break;
-                    case FEATURE_TYPE_HINGE:
-                        type = SidecarDisplayFeature.TYPE_HINGE;
-                        break;
-                    default: {
-                        Log.e(TAG, "Malformed feature type: " + featureType);
-                        continue;
-                    }
-                }
-
-                int left = Integer.parseInt(featureMatcher.group(2));
-                int top = Integer.parseInt(featureMatcher.group(3));
-                int right = Integer.parseInt(featureMatcher.group(4));
-                int bottom = Integer.parseInt(featureMatcher.group(5));
-                Rect featureRect = new Rect(left, top, right, bottom);
-                rotateRectToDisplayRotation(featureRect, displayId);
-                transformToWindowSpaceRect(featureRect, windowToken);
-                if (isNotZero(featureRect)) {
-                    SidecarDisplayFeature feature = new SidecarDisplayFeature();
-                    feature.setRect(featureRect);
-                    feature.setType(type);
-                    features.add(feature);
-                } else {
-                    Log.w(TAG, "Failed to adjust feature to window");
-                }
-            } catch (NumberFormatException e) {
-                Log.e(TAG, "Malformed feature description: " + featureString);
-            }
-        }
-        return features;
-    }
-
-    private static boolean isNotZero(Rect rect) {
-        return rect.height() > 0 || rect.width() > 0;
-    }
-
-    @Override
-    protected void onListenersChanged() {
-        if (mSettingsObserver == null) {
-            return;
-        }
-
-        if (hasListeners()) {
-            mSettingsObserver.registerObserversIfNeeded();
-        } else {
-            mSettingsObserver.unregisterObserversIfNeeded();
-        }
-    }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
index 0b4915e..e6f8388 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
@@ -28,7 +28,7 @@
      * an OEM by overriding this method.
      */
     public static SidecarInterface getSidecarImpl(Context context) {
-        return new SettingsSidecarImpl(context);
+        return new SampleSidecarImpl(context);
     }
 
     /**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
new file mode 100644
index 0000000..b74a2a4
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.util;
+
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+
+/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
+public class BaseDisplayFeature {
+    private final int mType;
+    private final int mState;
+    @NonNull
+    public final Rect mRect;
+
+    public BaseDisplayFeature(int type, int state, @NonNull Rect rect) {
+        this.mType = type;
+        this.mState = state;
+        if (rect.width() == 0 && rect.height() == 0) {
+            throw new IllegalArgumentException(
+                    "Display feature rectangle cannot have zero width and height simultaneously.");
+        }
+        this.mRect = rect;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    public int getState() {
+        return mState;
+    }
+
+    @NonNull
+    public Rect getRect() {
+        return mRect;
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
similarity index 62%
rename from libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
rename to libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
index e5b6cff..2a593f1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,30 +14,36 @@
  * limitations under the License.
  */
 
-package androidx.window.sidecar;
+package androidx.window.util;
 
-import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
 import android.app.Activity;
-import android.app.ActivityThread;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
-import android.os.IBinder;
 import android.view.DisplayInfo;
 import android.view.Surface;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-class SidecarHelper {
+/**
+ * Util class for both Sidecar and Extensions.
+ */
+public final class ExtensionHelper {
+
+    private ExtensionHelper() {
+        // Util class, no instances should be created.
+    }
+
     /**
-     * Rotate the input rectangle specified in default display orientation to the current display
+     * Rotates the input rectangle specified in default display orientation to the current display
      * rotation.
      */
-    static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) {
+    public static void rotateRectToDisplayRotation(int displayId, Rect inOutRect) {
         DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
         DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
         int rotation = displayInfo.rotation;
@@ -52,7 +58,7 @@
     }
 
     /**
-     * Rotate the input rectangle within parent bounds for a given delta.
+     * Rotates the input rectangle within parent bounds for a given delta.
      */
     private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight,
             @Surface.Rotation int delta) {
@@ -79,9 +85,9 @@
         }
     }
 
-    /** Transform rectangle from absolute coordinate space to the window coordinate space. */
-    static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) {
-        Rect windowRect = getWindowBounds(windowToken);
+    /** Transforms rectangle from absolute coordinate space to the window coordinate space. */
+    public static void transformToWindowSpaceRect(Activity activity, Rect inOutRect) {
+        Rect windowRect = getWindowBounds(activity);
         if (windowRect == null) {
             inOutRect.setEmpty();
             return;
@@ -95,32 +101,17 @@
     }
 
     /**
-     * Get the current window bounds in absolute coordinates.
-     * NOTE: Only works with Activity windows.
+     * Gets the current window bounds in absolute coordinates.
      */
     @Nullable
-    private static Rect getWindowBounds(IBinder windowToken) {
-        Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
-        return activity != null
-                ? activity.getWindowManager().getCurrentWindowMetrics().getBounds()
-                : null;
+    private static Rect getWindowBounds(@NonNull Activity activity) {
+        return activity.getWindowManager().getCurrentWindowMetrics().getBounds();
     }
 
     /**
-     * Check if this window is an Activity window that is in multi-window mode.
+     * Checks if both dimensions of the given rect are zero at the same time.
      */
-    static boolean isInMultiWindow(IBinder windowToken) {
-        Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
-        return activity != null && activity.isInMultiWindowMode();
-    }
-
-    /**
-     * Get the id of the parent display for the window.
-     * NOTE: Only works with Activity windows.
-     */
-    static int getWindowDisplay(IBinder windowToken) {
-        Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
-        return activity != null
-                ? activity.getWindowManager().getDefaultDisplay().getDisplayId() : INVALID_DISPLAY;
+    public static boolean isZero(@NonNull Rect rect) {
+        return rect.height() == 0 && rect.width() == 0;
     }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
new file mode 100644
index 0000000..6dd190c
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.util;
+
+import static androidx.window.util.ExtensionHelper.isZero;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Device and display feature state provider that uses Settings as the source.
+ */
+public final class SettingsConfigProvider extends ContentObserver {
+    private static final String TAG = "SettingsConfigProvider";
+    private static final String DEVICE_POSTURE = "device_posture";
+    private static final String DISPLAY_FEATURES = "display_features";
+
+    private static final Pattern FEATURE_PATTERN =
+            Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
+
+    private static final String FEATURE_TYPE_FOLD = "fold";
+    private static final String FEATURE_TYPE_HINGE = "hinge";
+
+    private final Uri mDevicePostureUri =
+            Settings.Global.getUriFor(DEVICE_POSTURE);
+    private final Uri mDisplayFeaturesUri =
+            Settings.Global.getUriFor(DISPLAY_FEATURES);
+    private final Context mContext;
+    private final ContentResolver mResolver;
+    private final StateChangeCallback mCallback;
+    private boolean mRegisteredObservers;
+
+    public SettingsConfigProvider(@NonNull Context context, @NonNull StateChangeCallback callback) {
+        super(new Handler(Looper.getMainLooper()));
+        mContext = context;
+        mResolver = context.getContentResolver();
+        mCallback = callback;
+    }
+
+    /**
+     * Registers the content observers for Settings keys that store device state and display feature
+     * configurations.
+     */
+    public void registerObserversIfNeeded() {
+        if (mRegisteredObservers) {
+            return;
+        }
+        mRegisteredObservers = true;
+        mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendants */,
+                this /* ContentObserver */);
+        mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */,
+                this /* ContentObserver */);
+    }
+
+    /**
+     * Unregisters the content observers that are tracking the state changes.
+     * @see #registerObserversIfNeeded()
+     */
+    public void unregisterObserversIfNeeded() {
+        if (!mRegisteredObservers) {
+            return;
+        }
+        mRegisteredObservers = false;
+        mResolver.unregisterContentObserver(this);
+    }
+
+    /**
+     * Gets the device posture int stored in Settings.
+     */
+    public int getDeviceState() {
+        return Settings.Global.getInt(mResolver, DEVICE_POSTURE,
+                0 /* POSTURE_UNKNOWN */);
+    }
+
+    /**
+     * Gets the list of all display feature configs stored in Settings. Uses a custom
+     * {@link BaseDisplayFeature} class to report the config to be translated for actual
+     * containers in Sidecar or Extensions.
+     */
+    public List<BaseDisplayFeature> getDisplayFeatures() {
+        List<BaseDisplayFeature> features = new ArrayList<>();
+        String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
+        if (TextUtils.isEmpty(displayFeaturesString)) {
+            displayFeaturesString = mContext.getResources().getString(
+                    R.string.config_display_features);
+        }
+        if (TextUtils.isEmpty(displayFeaturesString)) {
+            return features;
+        }
+        String[] featureStrings =  displayFeaturesString.split(";");
+
+        int deviceState = getDeviceState();
+
+        for (String featureString : featureStrings) {
+            Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString);
+            if (!featureMatcher.matches()) {
+                Log.e(TAG, "Malformed feature description format: " + featureString);
+                continue;
+            }
+            try {
+                String featureType = featureMatcher.group(1);
+                int type;
+                switch (featureType) {
+                    case FEATURE_TYPE_FOLD:
+                        type = 1 /* TYPE_FOLD */;
+                        break;
+                    case FEATURE_TYPE_HINGE:
+                        type = 2 /* TYPE_HINGE */;
+                        break;
+                    default: {
+                        Log.e(TAG, "Malformed feature type: " + featureType);
+                        continue;
+                    }
+                }
+
+                int left = Integer.parseInt(featureMatcher.group(2));
+                int top = Integer.parseInt(featureMatcher.group(3));
+                int right = Integer.parseInt(featureMatcher.group(4));
+                int bottom = Integer.parseInt(featureMatcher.group(5));
+                Rect featureRect = new Rect(left, top, right, bottom);
+                if (!isZero(featureRect)) {
+                    BaseDisplayFeature feature = new BaseDisplayFeature(type, deviceState,
+                            featureRect);
+                    features.add(feature);
+                } else {
+                    Log.w(TAG, "Read empty feature");
+                }
+            } catch (NumberFormatException e) {
+                Log.e(TAG, "Malformed feature description: " + featureString);
+            }
+        }
+        return features;
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        if (uri == null) {
+            return;
+        }
+
+        if (mDevicePostureUri.equals(uri)) {
+            mCallback.onDevicePostureChanged();
+            mCallback.onDisplayFeaturesChanged();
+            return;
+        }
+        if (mDisplayFeaturesUri.equals(uri)) {
+            mCallback.onDisplayFeaturesChanged();
+        }
+    }
+
+    /**
+     * Callback that notifies about device or display feature state changes.
+     */
+    public interface StateChangeCallback {
+        /**
+         * Notifies about the device state update.
+         */
+        void onDevicePostureChanged();
+
+        /**
+         * Notifies about the display feature config update.
+         */
+        void onDisplayFeaturesChanged();
+    }
+}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
new file mode 100644
index 0000000..7b306b0
--- /dev/null
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index 2cfb13e..9c3d84e 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -13,6 +13,12 @@
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
     },
+    "-1671119352": {
+      "message": " Delegate animation for %s to %s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TRANSITIONS",
+      "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
+    },
     "-1501874464": {
       "message": "Fullscreen Task Appeared: #%d",
       "level": "VERBOSE",
@@ -49,6 +55,24 @@
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
     },
+    "-1308483871": {
+      "message": " try handler %s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TRANSITIONS",
+      "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
+    },
+    "-1297259344": {
+      "message": " animated by %s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TRANSITIONS",
+      "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
+    },
+    "-1269886472": {
+      "message": "Transition %s doesn't have explicit remote, search filters for match for %s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TRANSITIONS",
+      "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
+    },
     "-1006733970": {
       "message": "Display added: %d",
       "level": "VERBOSE",
@@ -91,12 +115,24 @@
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/apppairs\/AppPairsController.java"
     },
+    "138343607": {
+      "message": " try firstHandler %s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TRANSITIONS",
+      "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
+    },
     "157713005": {
       "message": "Task info changed taskId=%d",
       "level": "VERBOSE",
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
     },
+    "214412327": {
+      "message": "RemoteTransition directly requested for %s: %s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TRANSITIONS",
+      "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
+    },
     "274140888": {
       "message": "Animate alpha: from=%d to=%d",
       "level": "VERBOSE",
@@ -115,6 +151,12 @@
       "group": "WM_SHELL_DRAG_AND_DROP",
       "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
     },
+    "410592459": {
+      "message": "Invalid root leash (%s): %s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TRANSITIONS",
+      "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
+    },
     "473543554": {
       "message": "%s onTaskAppeared Supported",
       "level": "VERBOSE",
@@ -139,6 +181,12 @@
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
     },
+    "707170340": {
+      "message": " animated by firstHandler",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TRANSITIONS",
+      "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
+    },
     "900599280": {
       "message": "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
       "level": "ERROR",
@@ -163,6 +211,12 @@
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/legacysplitscreen\/LegacySplitScreenTaskListener.java"
     },
+    "990371881": {
+      "message": " Checking filter %s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TRANSITIONS",
+      "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
+    },
     "1070270131": {
       "message": "onTransitionReady %s: %s",
       "level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index 7ea4689..255e4d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.animation
 
-import android.os.Looper
 import android.util.ArrayMap
 import android.util.Log
 import android.view.View
@@ -847,7 +846,7 @@
      * pass to [spring].
      */
     data class SpringConfig internal constructor(
-        internal var stiffness: Float,
+        var stiffness: Float,
         internal var dampingRatio: Float,
         internal var startVelocity: Float = 0f,
         internal var finalPosition: Float = UNSET
@@ -879,8 +878,8 @@
      */
     data class FlingConfig internal constructor(
         internal var friction: Float,
-        internal var min: Float,
-        internal var max: Float,
+        var min: Float,
+        var max: Float,
         internal var startVelocity: Float
     ) {
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index ffeabd8..40fdb97 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -39,6 +39,7 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -59,6 +60,8 @@
     private static final String TAG = "Bubble";
 
     private final String mKey;
+    @Nullable
+    private final String mGroupKey;
     private final Executor mMainExecutor;
 
     private long mLastUpdated;
@@ -165,6 +168,7 @@
         mMetadataShortcutId = shortcutInfo.getId();
         mShortcutInfo = shortcutInfo;
         mKey = key;
+        mGroupKey = null;
         mFlags = 0;
         mUser = shortcutInfo.getUserHandle();
         mPackageName = shortcutInfo.getPackage();
@@ -182,6 +186,7 @@
             final Bubbles.PendingIntentCanceledListener intentCancelListener,
             Executor mainExecutor) {
         mKey = entry.getKey();
+        mGroupKey = entry.getGroupKey();
         mSuppressionListener = listener;
         mIntentCancelListener = intent -> {
             if (mIntent != null) {
@@ -200,6 +205,14 @@
         return mKey;
     }
 
+    /**
+     * @see StatusBarNotification#getGroupKey()
+     * @return the group key for this bubble, if one exists.
+     */
+    public String getGroupKey() {
+        return mGroupKey;
+    }
+
     public UserHandle getUser() {
         return mUser;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 7538c8b..d73fc6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -771,7 +771,8 @@
                     // if the bubble is already active, there's no need to push it to overflow
                     return;
                 }
-                bubble.inflate((b) -> mBubbleData.overflowBubble(DISMISS_AGED, bubble),
+                bubble.inflate(
+                        (b) -> mBubbleData.overflowBubble(Bubbles.DISMISS_RELOAD_FROM_DISK, bubble),
                         mContext, this, mStackView, mBubbleIconFactory, true /* skipInflation */);
             });
             return null;
@@ -892,10 +893,7 @@
             return bubbleChildren;
         }
         for (Bubble bubble : mBubbleData.getActiveBubbles()) {
-            // TODO(178620678): Prevent calling into SysUI since this can be a part of a blocking
-            //                  call from SysUI to Shell
-            final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey());
-            if (entry != null && groupKey.equals(entry.getStatusBarNotification().getGroupKey())) {
+            if (bubble.getGroupKey() != null && groupKey.equals(bubble.getGroupKey())) {
                 bubbleChildren.add(bubble);
             }
         }
@@ -1064,8 +1062,8 @@
     private boolean isSummaryOfBubbles(BubbleEntry entry) {
         String groupKey = entry.getStatusBarNotification().getGroupKey();
         ArrayList<Bubble> bubbleChildren = getBubblesInGroup(groupKey);
-        boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
-                && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey()));
+        boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey)
+                && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey());
         boolean isSummary = entry.getStatusBarNotification().getNotification().isGroupSummary();
         return (isSuppressedSummary || isSummary) && !bubbleChildren.isEmpty();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 9d196ba..53b7537 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -542,7 +542,8 @@
     void overflowBubble(@DismissReason int reason, Bubble bubble) {
         if (bubble.getPendingIntentCanceled()
                 || !(reason == Bubbles.DISMISS_AGED
-                || reason == Bubbles.DISMISS_USER_GESTURE)) {
+                    || reason == Bubbles.DISMISS_USER_GESTURE
+                    || reason == Bubbles.DISMISS_RELOAD_FROM_DISK)) {
             return;
         }
         if (DEBUG_BUBBLE_DATA) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
index 3361c4c..c88a58b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
@@ -61,7 +61,10 @@
         BUBBLE_OVERFLOW_REMOVE_BLOCKED(490),
 
         @UiEvent(doc = "User selected the overflow.")
-        BUBBLE_OVERFLOW_SELECTED(600);
+        BUBBLE_OVERFLOW_SELECTED(600),
+
+        @UiEvent(doc = "Restore bubble to overflow after phone reboot.")
+        BUBBLE_OVERFLOW_RECOVER(691);
 
         private final int mId;
 
@@ -112,6 +115,8 @@
             log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
         } else if (r == Bubbles.DISMISS_USER_GESTURE) {
             log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
+        } else if (r == Bubbles.DISMISS_RELOAD_FROM_DISK) {
+            log(b, Event.BUBBLE_OVERFLOW_RECOVER);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index af421fa..d54be0e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1478,6 +1478,9 @@
      * Update bubble order and pointer position.
      */
     public void updateBubbleOrder(List<Bubble> bubbles) {
+        if (isExpansionAnimating()) {
+            return;
+        }
         final Runnable reorder = () -> {
             for (int i = 0; i < bubbles.size(); i++) {
                 Bubble bubble = bubbles.get(i);
@@ -1662,6 +1665,7 @@
         }
         beforeExpandedViewAnimation();
 
+        updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
         mBubbleContainer.setActiveController(mExpandedAnimationController);
         updateOverflowVisibility();
         updatePointerPosition();
@@ -1875,7 +1879,7 @@
                                 mExpandedBubble));
                     }
                     updateOverflowVisibility();
-
+                    updateBadgesAndZOrder(true /* setBadgeForCollapsedStack */);
                     afterExpandedViewAnimation();
                     if (previouslySelected != null) {
                         previouslySelected.setContentVisibility(false);
@@ -2623,7 +2627,6 @@
         }
 
         mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
-        updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
     }
 
     /**
@@ -2733,9 +2736,13 @@
      * @param action the user interaction enum.
      */
     private void logBubbleEvent(@Nullable BubbleViewProvider provider, int action) {
+        final String packageName =
+                (provider != null && provider instanceof Bubble)
+                    ? ((Bubble) provider).getPackageName()
+                    : "null";
         mBubbleData.logBubbleEvent(provider,
                 action,
-                mContext.getApplicationInfo().packageName,
+                packageName,
                 getBubbleCount(),
                 getBubbleIndex(provider),
                 getNormalizedXPosition(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 6102147..6a1026b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -54,7 +54,7 @@
             DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
             DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
             DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
-            DISMISS_NO_BUBBLE_UP})
+            DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK})
     @Target({FIELD, LOCAL_VARIABLE, PARAMETER})
     @interface DismissReason {}
 
@@ -72,6 +72,7 @@
     int DISMISS_SHORTCUT_REMOVED = 12;
     int DISMISS_PACKAGE_REMOVED = 13;
     int DISMISS_NO_BUBBLE_UP = 14;
+    int DISMISS_RELOAD_FROM_DISK = 15;
 
     /**
      * @return {@code true} if there is a bubble associated with the provided key and if its
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index e1f831e..73371e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -62,8 +62,8 @@
 
     private static final String TAG = "Bubbs.StackCtrl";
 
-    /** Values to use for animating bubbles in. */
-    private static final float ANIMATE_IN_STIFFNESS = 1000f;
+    /** Value to use for animating bubbles in and springing stack after fling. */
+    private static final float STACK_SPRING_STIFFNESS = 700f;
 
     /** Values to use for animating updated bubble to top of stack. */
     private static final float NEW_BUBBLE_START_SCALE = 0.5f;
@@ -80,20 +80,15 @@
 
     private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
             new PhysicsAnimator.SpringConfig(
-                    ANIMATE_IN_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY);
+                    STACK_SPRING_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY);
 
     /**
      * Friction applied to fling animations. Since the stack must land on one of the sides of the
      * screen, we want less friction horizontally so that the stack has a better chance of making it
      * to the side without needing a spring.
      */
-    private static final float FLING_FRICTION = 2.2f;
+    private static final float FLING_FRICTION = 1.9f;
 
-    /**
-     * Values to use for the stack spring animation used to spring the stack to its final position
-     * after a fling.
-     */
-    private static final int SPRING_AFTER_FLING_STIFFNESS = 750;
     private static final float SPRING_AFTER_FLING_DAMPING_RATIO = 0.85f;
 
     /** Sentinel value for unset position value. */
@@ -216,7 +211,7 @@
 
         @Override
         public void moveToBounds(@NonNull Rect bounds) {
-            springStack(bounds.left, bounds.top, SpringForce.STIFFNESS_LOW);
+            springStack(bounds.left, bounds.top, STACK_SPRING_STIFFNESS);
         }
 
         @NonNull
@@ -341,7 +336,7 @@
      * flings.
      */
     public void springStackAfterFling(float destinationX, float destinationY) {
-        springStack(destinationX, destinationY, SPRING_AFTER_FLING_STIFFNESS);
+        springStack(destinationX, destinationY, STACK_SPRING_STIFFNESS);
     }
 
     /**
@@ -371,7 +366,7 @@
 
         final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
         final float stiffness = Settings.Secure.getFloat(contentResolver, "bubble_stiffness",
-                SPRING_AFTER_FLING_STIFFNESS /* default */);
+                STACK_SPRING_STIFFNESS /* default */);
         final float dampingRatio = Settings.Secure.getFloat(contentResolver, "bubble_damping",
                 SPRING_AFTER_FLING_DAMPING_RATIO);
         final float friction = Settings.Secure.getFloat(contentResolver, "bubble_friction",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
index 00bd9e5..59374a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
@@ -19,7 +19,6 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ITaskStackListener;
 import android.content.ComponentName;
-import android.os.IBinder;
 import android.window.TaskSnapshot;
 
 import androidx.annotation.BinderThread;
@@ -85,6 +84,4 @@
     default void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { }
 
     default void onActivityRotation(int displayId) { }
-
-    default void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
index db34248..e94080a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
@@ -22,7 +22,6 @@
 import android.app.TaskStackListener;
 import android.content.ComponentName;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Message;
 import android.os.Trace;
 import android.util.Log;
@@ -54,13 +53,12 @@
     private static final int ON_TASK_MOVED_TO_FRONT = 12;
     private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 13;
     private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 14;
-    private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 15;
-    private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 16;
-    private static final int ON_TASK_DISPLAY_CHANGED = 17;
-    private static final int ON_TASK_LIST_UPDATED = 18;
-    private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 19;
-    private static final int ON_TASK_DESCRIPTION_CHANGED = 20;
-    private static final int ON_ACTIVITY_ROTATION = 21;
+    private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 15;
+    private static final int ON_TASK_DISPLAY_CHANGED = 16;
+    private static final int ON_TASK_LIST_UPDATED = 17;
+    private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 18;
+    private static final int ON_TASK_DESCRIPTION_CHANGED = 19;
+    private static final int ON_ACTIVITY_ROTATION = 20;
 
     /**
      * List of {@link TaskStackListenerCallback} registered from {@link #addListener}.
@@ -264,13 +262,6 @@
     }
 
     @Override
-    public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
-        mMainHandler.obtainMessage(ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId,
-                0 /* unused */,
-                activityToken).sendToTarget();
-    }
-
-    @Override
     public boolean handleMessage(Message msg) {
         synchronized (mTaskStackListeners) {
             switch (msg.what) {
@@ -383,13 +374,6 @@
                     }
                     break;
                 }
-                case ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED: {
-                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                        mTaskStackListeners.get(i).onSizeCompatModeActivityChanged(
-                                msg.arg1, (IBinder) msg.obj);
-                    }
-                    break;
-                }
                 case ON_BACK_PRESSED_ON_TASK_ROOT: {
                     for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                         mTaskStackListeners.get(i).onBackPressedOnTaskRoot(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 90992fb..45aa387 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -21,6 +21,7 @@
 import android.animation.RectEvaluator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
+import android.app.TaskInfo;
 import android.graphics.Rect;
 import android.view.Choreographer;
 import android.view.SurfaceControl;
@@ -99,18 +100,20 @@
 
     @SuppressWarnings("unchecked")
     @VisibleForTesting
-    public PipTransitionAnimator getAnimator(SurfaceControl leash,
+    public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
             Rect destinationBounds, float alphaStart, float alphaEnd) {
         if (mCurrentAnimator == null) {
             mCurrentAnimator = setupPipTransitionAnimator(
-                    PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
+                    PipTransitionAnimator.ofAlpha(taskInfo, leash, destinationBounds, alphaStart,
+                            alphaEnd));
         } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
                 && mCurrentAnimator.isRunning()) {
             mCurrentAnimator.updateEndValue(alphaEnd);
         } else {
             mCurrentAnimator.cancel();
             mCurrentAnimator = setupPipTransitionAnimator(
-                    PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
+                    PipTransitionAnimator.ofAlpha(taskInfo, leash, destinationBounds, alphaStart,
+                            alphaEnd));
         }
         return mCurrentAnimator;
     }
@@ -131,13 +134,13 @@
      * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
      */
     @VisibleForTesting
-    public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect baseBounds,
-            Rect startBounds, Rect endBounds, Rect sourceHintRect,
+    public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
+            Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
             @PipAnimationController.TransitionDirection int direction, float startingAngle) {
         if (mCurrentAnimator == null) {
             mCurrentAnimator = setupPipTransitionAnimator(
-                    PipTransitionAnimator.ofBounds(leash, startBounds, startBounds, endBounds,
-                            sourceHintRect, direction, 0 /* startingAngle */));
+                    PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds,
+                            endBounds, sourceHintRect, direction, 0 /* startingAngle */));
         } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
                 && mCurrentAnimator.isRunning()) {
             // If we are still animating the fade into pip, then just move the surface and ensure
@@ -152,8 +155,8 @@
         } else {
             mCurrentAnimator.cancel();
             mCurrentAnimator = setupPipTransitionAnimator(
-                    PipTransitionAnimator.ofBounds(leash, baseBounds, startBounds, endBounds,
-                            sourceHintRect, direction, startingAngle));
+                    PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds,
+                            endBounds, sourceHintRect, direction, startingAngle));
         }
         return mCurrentAnimator;
     }
@@ -177,18 +180,18 @@
         /**
          * Called when PiP animation is started.
          */
-        public void onPipAnimationStart(PipTransitionAnimator animator) {}
+        public void onPipAnimationStart(TaskInfo taskInfo, PipTransitionAnimator animator) {}
 
         /**
          * Called when PiP animation is ended.
          */
-        public void onPipAnimationEnd(SurfaceControl.Transaction tx,
+        public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx,
                 PipTransitionAnimator animator) {}
 
         /**
          * Called when PiP animation is cancelled.
          */
-        public void onPipAnimationCancel(PipTransitionAnimator animator) {}
+        public void onPipAnimationCancel(TaskInfo taskInfo, PipTransitionAnimator animator) {}
     }
 
     /**
@@ -198,6 +201,7 @@
     public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
             ValueAnimator.AnimatorUpdateListener,
             ValueAnimator.AnimatorListener {
+        private final TaskInfo mTaskInfo;
         private final SurfaceControl mLeash;
         private final @AnimationType int mAnimationType;
         private final Rect mDestinationBounds = new Rect();
@@ -213,9 +217,10 @@
         private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
         private @TransitionDirection int mTransitionDirection;
 
-        private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
-                Rect destinationBounds, T baseValue, T startValue, T endValue,
-                float startingAngle) {
+        private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash,
+                @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue,
+                T endValue, float startingAngle) {
+            mTaskInfo = taskInfo;
             mLeash = leash;
             mAnimationType = animationType;
             mDestinationBounds.set(destinationBounds);
@@ -234,7 +239,7 @@
             mCurrentValue = mStartValue;
             onStartTransaction(mLeash, newSurfaceControlTransaction());
             if (mPipAnimationCallback != null) {
-                mPipAnimationCallback.onPipAnimationStart(this);
+                mPipAnimationCallback.onPipAnimationStart(mTaskInfo, this);
             }
         }
 
@@ -250,14 +255,14 @@
             final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
             onEndTransaction(mLeash, tx, mTransitionDirection);
             if (mPipAnimationCallback != null) {
-                mPipAnimationCallback.onPipAnimationEnd(tx, this);
+                mPipAnimationCallback.onPipAnimationEnd(mTaskInfo, tx, this);
             }
         }
 
         @Override
         public void onAnimationCancel(Animator animation) {
             if (mPipAnimationCallback != null) {
-                mPipAnimationCallback.onPipAnimationCancel(this);
+                mPipAnimationCallback.onPipAnimationCancel(mTaskInfo, this);
             }
         }
 
@@ -368,9 +373,9 @@
         abstract void applySurfaceControlTransaction(SurfaceControl leash,
                 SurfaceControl.Transaction tx, float fraction);
 
-        static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash,
+        static PipTransitionAnimator<Float> ofAlpha(TaskInfo taskInfo, SurfaceControl leash,
                 Rect destinationBounds, float startValue, float endValue) {
-            return new PipTransitionAnimator<Float>(leash, ANIM_TYPE_ALPHA,
+            return new PipTransitionAnimator<Float>(taskInfo, leash, ANIM_TYPE_ALPHA,
                     destinationBounds, startValue, startValue, endValue, 0) {
                 @Override
                 void applySurfaceControlTransaction(SurfaceControl leash,
@@ -403,7 +408,7 @@
             };
         }
 
-        static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
+        static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash,
                 Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
                 @PipAnimationController.TransitionDirection int direction, float startingAngle) {
             // Just for simplicity we'll interpolate between the source rect hint insets and empty
@@ -427,7 +432,7 @@
             final Rect sourceInsets = new Rect(0, 0, 0, 0);
 
             // construct new Rect instances in case they are recycled
-            return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
+            return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
                     endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue),
                     startingAngle) {
                 private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index a8961ea..ac5d14c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -19,7 +19,9 @@
 import static android.util.TypedValue.COMPLEX_UNIT_DIP;
 
 import android.annotation.NonNull;
+import android.app.PictureInPictureParams;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -27,7 +29,6 @@
 import android.util.DisplayMetrics;
 import android.util.Size;
 import android.util.TypedValue;
-import android.view.DisplayInfo;
 import android.view.Gravity;
 
 import com.android.wm.shell.common.DisplayLayout;
@@ -142,11 +143,53 @@
                 true /* useCurrentMinEdgeSize */, false /* useCurrentSize */);
     }
 
+    /**
+     *
+     * Get the smallest/most minimal size allowed.
+     */
+    public Size getMinimalSize(ActivityInfo activityInfo) {
+        if (activityInfo == null || activityInfo.windowLayout == null) {
+            return null;
+        }
+        final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout;
+        // -1 will be populated if an activity specifies defaultWidth/defaultHeight in <layout>
+        // without minWidth/minHeight
+        if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
+            return new Size(windowLayout.minWidth, windowLayout.minHeight);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the source hint rect if it is valid (if provided and is contained by the current
+     * task bounds).
+     */
+    public static Rect getValidSourceHintRect(PictureInPictureParams params, Rect sourceBounds) {
+        final Rect sourceHintRect = params != null && params.hasSourceBoundsHint()
+                ? params.getSourceRectHint()
+                : null;
+        if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) {
+            return sourceHintRect;
+        }
+        return null;
+    }
+
     public float getDefaultAspectRatio() {
         return mDefaultAspectRatio;
     }
 
     /**
+     *
+     * Give the aspect ratio if the supplied PiP params have one, or else return default.
+     */
+    public float getAspectRatioOrDefault(
+            @android.annotation.Nullable PictureInPictureParams params) {
+        return params != null && params.hasSetAspectRatio()
+                ? params.getAspectRatio()
+                : getDefaultAspectRatio();
+    }
+
+    /**
      * @return whether the given {@param aspectRatio} is valid.
      */
     private boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index b112c51..cb39b4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -25,7 +25,6 @@
 import android.graphics.Rect;
 import android.util.Size;
 import android.view.Display;
-import android.view.DisplayInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.function.TriConsumer;
@@ -344,6 +343,16 @@
         }
     }
 
+    /**
+     * Initialize states when first entering PiP.
+     */
+    public void setBoundsStateForEntry(ComponentName componentName, float aspectRatio,
+            Size overrideMinSize) {
+        setLastPipComponentName(componentName);
+        setAspectRatio(aspectRatio);
+        setOverrideMinSize(overrideMinSize);
+    }
+
     /** Returns whether the shelf is currently showing. */
     public boolean isShelfShowing() {
         return mIsShelfShowing;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b7958b7..fb83006 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -41,10 +41,10 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.PictureInPictureParams;
+import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -54,7 +54,6 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Rational;
-import android.util.Size;
 import android.view.Display;
 import android.view.SurfaceControl;
 import android.window.TaskOrganizer;
@@ -63,7 +62,6 @@
 import android.window.WindowContainerTransactionCallback;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.jank.InteractionJankMonitor;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
@@ -71,10 +69,10 @@
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.transition.Transitions;
+
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -132,8 +130,8 @@
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
     private final @NonNull PipMenuController mPipMenuController;
     private final PipAnimationController mPipAnimationController;
+    private final PipTransitionController mPipTransitionController;
     private final PipUiEventLogger mPipUiEventLoggerLogger;
-    private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
     private final int mEnterExitAnimationDuration;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
     private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
@@ -145,7 +143,8 @@
     private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
             new PipAnimationController.PipAnimationCallback() {
         @Override
-        public void onPipAnimationStart(PipAnimationController.PipTransitionAnimator animator) {
+        public void onPipAnimationStart(TaskInfo taskInfo,
+                PipAnimationController.PipTransitionAnimator animator) {
             final int direction = animator.getTransitionDirection();
             if (direction == TRANSITION_DIRECTION_TO_PIP) {
                 // TODO (b//169221267): Add jank listener for transactions without buffer updates.
@@ -156,7 +155,7 @@
         }
 
         @Override
-        public void onPipAnimationEnd(SurfaceControl.Transaction tx,
+        public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx,
                 PipAnimationController.PipTransitionAnimator animator) {
             final int direction = animator.getTransitionDirection();
             finishResize(tx, animator.getDestinationBounds(), direction,
@@ -170,7 +169,8 @@
         }
 
         @Override
-        public void onPipAnimationCancel(PipAnimationController.PipTransitionAnimator animator) {
+        public void onPipAnimationCancel(TaskInfo taskInfo,
+                PipAnimationController.PipTransitionAnimator animator) {
             sendOnPipTransitionCancelled(animator.getTransitionDirection());
         }
     };
@@ -202,7 +202,9 @@
     public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState,
             @NonNull PipBoundsAlgorithm boundsHandler,
             @NonNull PipMenuController pipMenuController,
+            @NonNull PipAnimationController pipAnimationController,
             @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
+            @NonNull PipTransitionController pipTransitionController,
             Optional<LegacySplitScreen> splitScreenOptional,
             @NonNull DisplayController displayController,
             @NonNull PipUiEventLogger pipUiEventLogger,
@@ -211,10 +213,11 @@
         mPipBoundsState = pipBoundsState;
         mPipBoundsAlgorithm = boundsHandler;
         mPipMenuController = pipMenuController;
+        mPipTransitionController = pipTransitionController;
         mEnterExitAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
         mSurfaceTransactionHelper = surfaceTransactionHelper;
-        mPipAnimationController = new PipAnimationController(mSurfaceTransactionHelper);
+        mPipAnimationController = pipAnimationController;
         mPipUiEventLoggerLogger = pipUiEventLogger;
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
         mSplitScreenOptional = splitScreenOptional;
@@ -246,13 +249,6 @@
     }
 
     /**
-     * Registers {@link PipTransitionCallback} to receive transition callbacks.
-     */
-    public void registerPipTransitionCallback(PipTransitionCallback callback) {
-        mPipTransitionCallbacks.add(callback);
-    }
-
-    /**
      * Registers a callback when a display change has been detected when we enter PiP.
      */
     public void registerOnDisplayIdChangeCallback(IntConsumer onDisplayIdChangeCallback) {
@@ -275,7 +271,7 @@
     public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
             PictureInPictureParams pictureInPictureParams) {
         mInSwipePipToHomeTransition = true;
-        sendOnPipTransitionStarted(componentName, TRANSITION_DIRECTION_TO_PIP);
+        sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP);
         setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo);
         // disable the conflicting transaction from fixed rotation, see also
         // onFixedRotationStarted and onFixedRotationFinished
@@ -296,9 +292,9 @@
 
     private void setBoundsStateForEntry(ComponentName componentName, PictureInPictureParams params,
             ActivityInfo activityInfo) {
-        mPipBoundsState.setLastPipComponentName(componentName);
-        mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(params));
-        mPipBoundsState.setOverrideMinSize(getMinimalSize(activityInfo));
+        mPipBoundsState.setBoundsStateForEntry(componentName,
+                mPipBoundsAlgorithm.getAspectRatioOrDefault(params),
+                mPipBoundsAlgorithm.getMinimalSize(activityInfo));
     }
 
     /**
@@ -362,8 +358,8 @@
                         t.apply();
                         // Make sure to grab the latest source hint rect as it could have been
                         // updated right after applying the windowing mode change.
-                        final Rect sourceHintRect = getValidSourceHintRect(mPictureInPictureParams,
-                                destinationBounds);
+                        final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+                                mPictureInPictureParams, destinationBounds);
                         scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
                                 0 /* startingAngle */, sourceHintRect, direction,
                                 animationDurationMs, null /* updateBoundsCallback */);
@@ -398,7 +394,7 @@
 
         // removePipImmediately is expected when the following animation finishes.
         mPipAnimationController
-                .getAnimator(mLeash, mPipBoundsState.getBounds(), 1f, 0f)
+                .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), 1f, 0f)
                 .setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(mEnterExitAnimationDuration)
@@ -470,10 +466,17 @@
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
 
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
+                mPipMenuController.attach(mLeash);
+            }
+            return;
+        }
+
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
             mPipMenuController.attach(mLeash);
-            final Rect sourceHintRect = getValidSourceHintRect(info.pictureInPictureParams,
-                    currentBounds);
+            final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+                    info.pictureInPictureParams, currentBounds);
             scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */,
                     sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
                     null /* updateBoundsCallback */);
@@ -486,21 +489,6 @@
         }
     }
 
-    /**
-     * Returns the source hint rect if it is valid (if provided and is contained by the current
-     * task bounds).
-     */
-    private Rect getValidSourceHintRect(PictureInPictureParams params, Rect sourceBounds) {
-        final Rect sourceHintRect = params != null
-                && params.hasSourceBoundsHint()
-                ? params.getSourceRectHint()
-                : null;
-        if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) {
-            return sourceHintRect;
-        }
-        return null;
-    }
-
     @VisibleForTesting
     void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) {
         // If we are fading the PIP in, then we should move the pip to the final location as
@@ -512,7 +500,7 @@
         tx.apply();
         applyEnterPipSyncTransaction(destinationBounds, () -> {
             mPipAnimationController
-                    .getAnimator(mLeash, destinationBounds, 0f, 1f)
+                    .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f)
                     .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
                     .setPipAnimationCallback(mPipAnimationCallback)
                     .setDuration(durationMs)
@@ -547,19 +535,10 @@
 
     private void sendOnPipTransitionStarted(
             @PipAnimationController.TransitionDirection int direction) {
-        sendOnPipTransitionStarted(mTaskInfo.baseActivity, direction);
-    }
-
-    private void sendOnPipTransitionStarted(ComponentName componentName,
-            @PipAnimationController.TransitionDirection int direction) {
         if (direction == TRANSITION_DIRECTION_TO_PIP) {
             mState = State.ENTERING_PIP;
         }
-        final Rect pipBounds = mPipBoundsState.getBounds();
-        for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
-            final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
-            callback.onPipTransitionStarted(componentName, direction, pipBounds);
-        }
+        mPipTransitionController.sendOnPipTransitionStarted(direction);
     }
 
     private void sendOnPipTransitionFinished(
@@ -567,18 +546,12 @@
         if (direction == TRANSITION_DIRECTION_TO_PIP) {
             mState = State.ENTERED_PIP;
         }
-        for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
-            final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
-            callback.onPipTransitionFinished(mTaskInfo.baseActivity, direction);
-        }
+        mPipTransitionController.sendOnPipTransitionFinished(direction);
     }
 
     private void sendOnPipTransitionCancelled(
             @PipAnimationController.TransitionDirection int direction) {
-        for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
-            final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
-            callback.onPipTransitionCanceled(mTaskInfo.baseActivity, direction);
-        }
+        mPipTransitionController.sendOnPipTransitionCancelled(direction);
     }
 
     /**
@@ -616,7 +589,8 @@
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
         Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
         mPipBoundsState.setLastPipComponentName(info.topActivity);
-        mPipBoundsState.setOverrideMinSize(getMinimalSize(info.topActivityInfo));
+        mPipBoundsState.setOverrideMinSize(
+                mPipBoundsAlgorithm.getMinimalSize(info.topActivityInfo));
         final PictureInPictureParams newParams = info.pictureInPictureParams;
         if (newParams == null || !applyPictureInPictureParams(newParams)) {
             Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams);
@@ -1081,33 +1055,14 @@
         Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
                 ? mPipBoundsState.getBounds() : currentBounds;
         mPipAnimationController
-                .getAnimator(mLeash, baseBounds, currentBounds, destinationBounds, sourceHintRect,
-                        direction, startingAngle)
+                .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds,
+                        sourceHintRect, direction, startingAngle)
                 .setTransitionDirection(direction)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(durationMs)
                 .start();
     }
 
-    private Size getMinimalSize(ActivityInfo activityInfo) {
-        if (activityInfo == null || activityInfo.windowLayout == null) {
-            return null;
-        }
-        final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout;
-        // -1 will be populated if an activity specifies defaultWidth/defaultHeight in <layout>
-        // without minWidth/minHeight
-        if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
-            return new Size(windowLayout.minWidth, windowLayout.minHeight);
-        }
-        return null;
-    }
-
-    private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
-        return params == null || !params.hasSetAspectRatio()
-                ? mPipBoundsAlgorithm.getDefaultAspectRatio()
-                : params.getAspectRatio();
-    }
-
     /**
      * Sync with {@link LegacySplitScreen} on destination bounds if PiP is going to split screen.
      *
@@ -1157,24 +1112,4 @@
     public String toString() {
         return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_PIP);
     }
-
-    /**
-     * Callback interface for PiP transitions (both from and to PiP mode)
-     */
-    public interface PipTransitionCallback {
-        /**
-         * Callback when the pip transition is started.
-         */
-        void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds);
-
-        /**
-         * Callback when the pip transition is finished.
-         */
-        void onPipTransitionFinished(ComponentName activity, int direction);
-
-        /**
-         * Callback when the pip transition is cancelled.
-         */
-        void onPipTransitionCanceled(ComponentName activity, int direction);
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
new file mode 100644
index 0000000..91e8c99
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
+import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
+import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
+import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+
+import android.app.TaskInfo;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.transition.Transitions;
+
+/**
+ * Implementation of transitions for PiP on phone. Responsible for enter (alpha, bounds) and
+ * exit animation.
+ */
+public class PipTransition extends PipTransitionController {
+
+    private final int mEnterExitAnimationDuration;
+    private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+    private Transitions.TransitionFinishCallback mFinishCallback;
+
+    public PipTransition(Context context,
+            PipBoundsState pipBoundsState, PipMenuController pipMenuController,
+            PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipAnimationController pipAnimationController,
+            Transitions transitions,
+            @NonNull ShellTaskOrganizer shellTaskOrganizer) {
+        super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
+                pipAnimationController, transitions, shellTaskOrganizer);
+        mEnterExitAnimationDuration = context.getResources()
+                .getInteger(R.integer.config_pipResizeAnimationDuration);
+    }
+
+    @Override
+    public boolean startAnimation(@android.annotation.NonNull IBinder transition,
+            @android.annotation.NonNull TransitionInfo info,
+            @android.annotation.NonNull SurfaceControl.Transaction t,
+            @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (change.getTaskInfo() != null
+                    && change.getTaskInfo().configuration.windowConfiguration.getWindowingMode()
+                    == WINDOWING_MODE_PINNED) {
+                mFinishCallback = finishCallback;
+                return startEnterAnimation(change.getTaskInfo(), change.getLeash(), t);
+            }
+        }
+        return false;
+    }
+
+    @Nullable
+    @Override
+    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+            @NonNull TransitionRequestInfo request) {
+        return null;
+    }
+
+    @Override
+    public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
+            @PipAnimationController.TransitionDirection int direction,
+            SurfaceControl.Transaction tx) {
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        prepareFinishResizeTransaction(taskInfo, destinationBounds,
+                direction, tx, wct);
+        mFinishCallback.onTransitionFinished(wct, null);
+        finishResizeForMenu(destinationBounds);
+    }
+
+    private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
+            final SurfaceControl.Transaction t) {
+        setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
+                taskInfo.topActivityInfo);
+        final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+        final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
+        PipAnimationController.PipTransitionAnimator animator;
+        if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
+            final Rect sourceHintRect =
+                    PipBoundsAlgorithm.getValidSourceHintRect(
+                            taskInfo.pictureInPictureParams, currentBounds);
+            animator = mPipAnimationController.getAnimator(taskInfo, leash,
+                    currentBounds, currentBounds, destinationBounds, sourceHintRect,
+                    TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */);
+        } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+            t.setAlpha(leash, 0f);
+            t.apply();
+            animator = mPipAnimationController.getAnimator(taskInfo, leash,
+                    destinationBounds, 0f, 1f);
+            mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+        } else {
+            throw new RuntimeException("Unrecognized animation type: "
+                    + mOneShotAnimationType);
+        }
+        animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
+                .setPipAnimationCallback(mPipAnimationCallback)
+                .setDuration(mEnterExitAnimationDuration)
+                .start();
+        return true;
+    }
+
+    private void finishResizeForMenu(Rect destinationBounds) {
+        mPipMenuController.movePipMenu(null, null, destinationBounds);
+        mPipMenuController.updateMenuBounds(destinationBounds);
+    }
+
+    private void prepareFinishResizeTransaction(TaskInfo taskInfo, Rect destinationBounds,
+            @PipAnimationController.TransitionDirection int direction,
+            SurfaceControl.Transaction tx,
+            WindowContainerTransaction wct) {
+        Rect taskBounds = null;
+        if (isInPipDirection(direction)) {
+            // If we are animating from fullscreen using a bounds animation, then reset the
+            // activity windowing mode set by WM, and set the task bounds to the final bounds
+            taskBounds = destinationBounds;
+            wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
+            wct.scheduleFinishEnterPip(taskInfo.token, destinationBounds);
+        } else if (isOutPipDirection(direction)) {
+            // If we are animating to fullscreen, then we need to reset the override bounds
+            // on the task to ensure that the task "matches" the parent's bounds.
+            taskBounds = (direction == TRANSITION_DIRECTION_LEAVE_PIP)
+                    ? null : destinationBounds;
+            wct.setWindowingMode(taskInfo.token, getOutPipWindowingMode());
+            // Simply reset the activity mode set prior to the animation running.
+            wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
+        }
+
+        wct.setBounds(taskInfo.token, taskBounds);
+        wct.setBoundsChangeTransaction(taskInfo.token, tx);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
new file mode 100644
index 0000000..d801c91
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
+
+import android.app.PictureInPictureParams;
+import android.app.TaskInfo;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.SurfaceControl;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Responsible supplying PiP Transitions.
+ */
+public abstract class PipTransitionController implements Transitions.TransitionHandler {
+
+    protected final PipAnimationController mPipAnimationController;
+    protected final PipBoundsAlgorithm mPipBoundsAlgorithm;
+    protected final PipBoundsState mPipBoundsState;
+    protected final ShellTaskOrganizer mShellTaskOrganizer;
+    protected final PipMenuController mPipMenuController;
+    private final Handler mMainHandler;
+    private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
+
+    protected final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
+            new PipAnimationController.PipAnimationCallback() {
+                @Override
+                public void onPipAnimationStart(TaskInfo taskInfo,
+                        PipAnimationController.PipTransitionAnimator animator) {
+                    final int direction = animator.getTransitionDirection();
+                    if (direction == TRANSITION_DIRECTION_TO_PIP) {
+                        // TODO (b//169221267): Add jank listener for transactions without buffer
+                        //  updates.
+                        //InteractionJankMonitor.getInstance().begin(
+                        //        InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP, 2000);
+                    }
+                    sendOnPipTransitionStarted(direction);
+                }
+
+                @Override
+                public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx,
+                        PipAnimationController.PipTransitionAnimator animator) {
+                    final int direction = animator.getTransitionDirection();
+                    mPipBoundsState.setBounds(animator.getDestinationBounds());
+                    if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
+                        return;
+                    }
+                    onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx);
+                    sendOnPipTransitionFinished(direction);
+                    if (direction == TRANSITION_DIRECTION_TO_PIP) {
+                        // TODO (b//169221267): Add jank listener for transactions without buffer
+                        //  updates.
+                        //InteractionJankMonitor.getInstance().end(
+                        //        InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP);
+                    }
+                }
+
+                @Override
+                public void onPipAnimationCancel(TaskInfo taskInfo,
+                        PipAnimationController.PipTransitionAnimator animator) {
+                    sendOnPipTransitionCancelled(animator.getTransitionDirection());
+                }
+            };
+
+    /**
+     * Called when transition is about to finish. This is usually for performing tasks such as
+     * applying WindowContainerTransaction to finalize the PiP bounds and send to the framework.
+     */
+    public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
+            @PipAnimationController.TransitionDirection int direction,
+            SurfaceControl.Transaction tx) {
+    }
+
+    public PipTransitionController(PipBoundsState pipBoundsState,
+            PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipAnimationController pipAnimationController, Transitions transitions,
+            @android.annotation.NonNull ShellTaskOrganizer shellTaskOrganizer) {
+        mPipBoundsState = pipBoundsState;
+        mPipMenuController = pipMenuController;
+        mShellTaskOrganizer = shellTaskOrganizer;
+        mPipBoundsAlgorithm = pipBoundsAlgorithm;
+        mPipAnimationController = pipAnimationController;
+        mMainHandler = new Handler(Looper.getMainLooper());
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            transitions.addHandler(this);
+        }
+    }
+
+    /**
+     * Registers {@link PipTransitionCallback} to receive transition callbacks.
+     */
+    public void registerPipTransitionCallback(PipTransitionCallback callback) {
+        mPipTransitionCallbacks.add(callback);
+    }
+
+    protected void sendOnPipTransitionStarted(
+            @PipAnimationController.TransitionDirection int direction) {
+        final Rect pipBounds = mPipBoundsState.getBounds();
+        for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+            final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+            callback.onPipTransitionStarted(direction, pipBounds);
+        }
+    }
+
+    protected void sendOnPipTransitionFinished(
+            @PipAnimationController.TransitionDirection int direction) {
+        for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+            final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+            callback.onPipTransitionFinished(direction);
+        }
+    }
+
+    protected void sendOnPipTransitionCancelled(
+            @PipAnimationController.TransitionDirection int direction) {
+        for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+            final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+            callback.onPipTransitionCanceled(direction);
+        }
+    }
+
+    /**
+     * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined
+     * and can be overridden to restore to an alternate windowing mode.
+     */
+    public int getOutPipWindowingMode() {
+        // By default, simply reset the windowing mode to undefined.
+        return WINDOWING_MODE_UNDEFINED;
+    }
+
+    protected void setBoundsStateForEntry(ComponentName componentName,
+            PictureInPictureParams params,
+            ActivityInfo activityInfo) {
+        mPipBoundsState.setBoundsStateForEntry(componentName,
+                mPipBoundsAlgorithm.getAspectRatioOrDefault(params),
+                mPipBoundsAlgorithm.getMinimalSize(activityInfo));
+    }
+
+    /**
+     * Callback interface for PiP transitions (both from and to PiP mode)
+     */
+    public interface PipTransitionCallback {
+        /**
+         * Callback when the pip transition is started.
+         */
+        void onPipTransitionStarted(int direction, Rect pipBounds);
+
+        /**
+         * Callback when the pip transition is finished.
+         */
+        void onPipTransitionFinished(int direction);
+
+        /**
+         * Callback when the pip transition is cancelled.
+         */
+        void onPipTransitionCanceled(int direction);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index c06f9d0..c3970e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -61,6 +61,7 @@
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipUtils;
 
 import java.io.PrintWriter;
@@ -69,7 +70,7 @@
 /**
  * Manages the picture-in-picture (PIP) UI and states for Phones.
  */
-public class PipController implements PipTaskOrganizer.PipTransitionCallback {
+public class PipController implements PipTransitionController.PipTransitionCallback {
     private static final String TAG = "PipController";
 
     private Context mContext;
@@ -82,6 +83,7 @@
     private PipBoundsAlgorithm mPipBoundsAlgorithm;
     private PipBoundsState mPipBoundsState;
     private PipTouchHandler mTouchHandler;
+    private PipTransitionController mPipTransitionController;
     protected final PipImpl mImpl = new PipImpl();
 
     private final Rect mTmpInsetBounds = new Rect();
@@ -214,7 +216,6 @@
         }
     }
 
-
     /**
      * Instantiates {@link PipController}, returns {@code null} if the feature not supported.
      */
@@ -223,7 +224,8 @@
             PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
             PipBoundsState pipBoundsState, PipMediaController pipMediaController,
             PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
-            PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
+            PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
+            WindowManagerShellWrapper windowManagerShellWrapper,
             TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) {
         if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
             Slog.w(TAG, "Device doesn't support Pip feature");
@@ -232,7 +234,8 @@
 
         return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
                 pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer,
-                pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor)
+                pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
+                taskStackListener, mainExecutor)
                 .mImpl;
     }
 
@@ -245,6 +248,7 @@
             PhonePipMenuController phonePipMenuController,
             PipTaskOrganizer pipTaskOrganizer,
             PipTouchHandler pipTouchHandler,
+            PipTransitionController pipTransitionController,
             WindowManagerShellWrapper windowManagerShellWrapper,
             TaskStackListenerImpl taskStackListener,
             ShellExecutor mainExecutor
@@ -266,9 +270,10 @@
         mMenuController = phonePipMenuController;
         mTouchHandler = pipTouchHandler;
         mAppOpsListener = pipAppOpsListener;
+        mPipTransitionController = pipTransitionController;
         mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
                 INPUT_CONSUMER_PIP, mainExecutor);
-        mPipTaskOrganizer.registerPipTransitionCallback(this);
+        mPipTransitionController.registerPipTransitionCallback(this);
         mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
             mPipBoundsState.setDisplayId(displayId);
             onDisplayChanged(displayController.getDisplayLayout(displayId),
@@ -489,7 +494,7 @@
     }
 
     @Override
-    public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
+    public void onPipTransitionStarted(int direction, Rect pipBounds) {
         if (isOutPipDirection(direction)) {
             // Exiting PIP, save the reentry state to restore to when re-entering.
             saveReentryState(pipBounds);
@@ -514,12 +519,12 @@
     }
 
     @Override
-    public void onPipTransitionFinished(ComponentName activity, int direction) {
+    public void onPipTransitionFinished(int direction) {
         onPipTransitionFinishedOrCanceled(direction);
     }
 
     @Override
-    public void onPipTransitionCanceled(ComponentName activity, int direction) {
+    public void onPipTransitionCanceled(int direction) {
         onPipTransitionFinishedOrCanceled(direction);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index fd4ea61..b19dcae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -22,7 +22,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -44,6 +43,7 @@
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
 
 import java.util.function.Consumer;
 
@@ -152,13 +152,13 @@
      */
     private Runnable mPostPipTransitionCallback;
 
-    private final PipTaskOrganizer.PipTransitionCallback mPipTransitionCallback =
-            new PipTaskOrganizer.PipTransitionCallback() {
+    private final PipTransitionController.PipTransitionCallback mPipTransitionCallback =
+            new PipTransitionController.PipTransitionCallback() {
         @Override
-        public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {}
+        public void onPipTransitionStarted(int direction, Rect pipBounds) {}
 
         @Override
-        public void onPipTransitionFinished(ComponentName activity, int direction) {
+        public void onPipTransitionFinished(int direction) {
             if (mPostPipTransitionCallback != null) {
                 mPostPipTransitionCallback.run();
                 mPostPipTransitionCallback = null;
@@ -166,20 +166,20 @@
         }
 
         @Override
-        public void onPipTransitionCanceled(ComponentName activity, int direction) {}
+        public void onPipTransitionCanceled(int direction) {}
     };
 
     public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
             PipTaskOrganizer pipTaskOrganizer, PhonePipMenuController menuController,
-            PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator,
-            ShellExecutor mainExecutor) {
+            PipSnapAlgorithm snapAlgorithm, PipTransitionController pipTransitionController,
+            FloatingContentCoordinator floatingContentCoordinator, ShellExecutor mainExecutor) {
         mContext = context;
         mPipTaskOrganizer = pipTaskOrganizer;
         mPipBoundsState = pipBoundsState;
         mMenuController = menuController;
         mSnapAlgorithm = snapAlgorithm;
         mFloatingContentCoordinator = floatingContentCoordinator;
-        mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
+        pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback);
         mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
                 mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 8fb358a..b91ba34 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -64,6 +64,7 @@
     private static final String TAG = "PipResizeGestureHandler";
     private static final int PINCH_RESIZE_SNAP_DURATION = 250;
     private static final int PINCH_RESIZE_MAX_ANGLE_ROTATION = 45;
+    private static final float PINCH_RESIZE_AUTO_MAX_RATIO = 0.9f;
 
     private final Context mContext;
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
@@ -539,6 +540,11 @@
             // position correctly. Drag-resize does not need to move, so just finalize resize.
             if (mUsingPinchToZoom) {
                 final Rect startBounds = new Rect(mLastResizeBounds);
+                // If user resize is pretty close to max size, just auto resize to max.
+                if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
+                        || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
+                    mLastResizeBounds.set(0, 0, mMaxSize.x, mMaxSize.y);
+                }
                 mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds,
                         mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds()));
                 mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 3cb3ae8..e69c6f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -51,6 +51,7 @@
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipUiEventLogger;
 
 import java.io.PrintWriter;
@@ -156,6 +157,7 @@
             PipBoundsAlgorithm pipBoundsAlgorithm,
             @NonNull PipBoundsState pipBoundsState,
             PipTaskOrganizer pipTaskOrganizer,
+            PipTransitionController pipTransitionController,
             FloatingContentCoordinator floatingContentCoordinator,
             PipUiEventLogger pipUiEventLogger,
             ShellExecutor mainExecutor) {
@@ -168,7 +170,7 @@
         mMenuController.addListener(new PipMenuListener());
         mGesture = new DefaultPipTouchGesture();
         mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer,
-                mMenuController, mPipBoundsAlgorithm.getSnapAlgorithm(),
+                mMenuController, mPipBoundsAlgorithm.getSnapAlgorithm(), pipTransitionController,
                 floatingContentCoordinator, mainExecutor);
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 75fc9f5..56f183f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -46,6 +46,7 @@
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -53,7 +54,7 @@
 /**
  * Manages the picture-in-picture (PIP) UI and states.
  */
-public class TvPipController implements PipTaskOrganizer.PipTransitionCallback,
+public class TvPipController implements PipTransitionController.PipTransitionCallback,
         TvPipMenuController.Delegate, TvPipNotificationController.Delegate {
     private static final String TAG = "TvPipController";
     static final boolean DEBUG = true;
@@ -105,6 +106,7 @@
             PipBoundsState pipBoundsState,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipTaskOrganizer pipTaskOrganizer,
+            PipTransitionController pipTransitionController,
             TvPipMenuController tvPipMenuController,
             PipMediaController pipMediaController,
             TvPipNotificationController pipNotificationController,
@@ -116,6 +118,7 @@
                 pipBoundsState,
                 pipBoundsAlgorithm,
                 pipTaskOrganizer,
+                pipTransitionController,
                 tvPipMenuController,
                 pipMediaController,
                 pipNotificationController,
@@ -129,6 +132,7 @@
             PipBoundsState pipBoundsState,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipTaskOrganizer pipTaskOrganizer,
+            PipTransitionController pipTransitionController,
             TvPipMenuController tvPipMenuController,
             PipMediaController pipMediaController,
             TvPipNotificationController pipNotificationController,
@@ -152,7 +156,7 @@
         mTvPipMenuController.setDelegate(this);
 
         mPipTaskOrganizer = pipTaskOrganizer;
-        mPipTaskOrganizer.registerPipTransitionCallback(this);
+        pipTransitionController.registerPipTransitionCallback(this);
 
         loadConfigurations();
 
@@ -302,17 +306,17 @@
     }
 
     @Override
-    public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
+    public void onPipTransitionStarted(int direction, Rect pipBounds) {
         if (DEBUG) Log.d(TAG, "onPipTransition_Started(), state=" + stateToName(mState));
     }
 
     @Override
-    public void onPipTransitionCanceled(ComponentName activity, int direction) {
+    public void onPipTransitionCanceled(int direction) {
         if (DEBUG) Log.d(TAG, "onPipTransition_Canceled(), state=" + stateToName(mState));
     }
 
     @Override
-    public void onPipTransitionFinished(ComponentName activity, int direction) {
+    public void onPipTransitionFinished(int direction) {
         if (DEBUG) Log.d(TAG, "onPipTransition_Finished(), state=" + stateToName(mState));
 
         if (mState == STATE_PIP_MENU) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
new file mode 100644
index 0000000..b7caf72
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.tv;
+
+import android.app.TaskInfo;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.transition.Transitions;
+
+/**
+ * PiP Transition for TV.
+ * TODO: Implement animation once TV is using Transitions.
+ */
+public class TvPipTransition extends PipTransitionController {
+    public TvPipTransition(PipBoundsState pipBoundsState,
+            PipMenuController pipMenuController,
+            PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipAnimationController pipAnimationController,
+            Transitions transitions,
+            @NonNull ShellTaskOrganizer shellTaskOrganizer) {
+        super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, pipAnimationController,
+                transitions, shellTaskOrganizer);
+    }
+
+    @Override
+    public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, int direction,
+            SurfaceControl.Transaction tx) {
+
+    }
+
+    @Override
+    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction t,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        return false;
+    }
+
+    @Nullable
+    @Override
+    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+            @NonNull TransitionRequestInfo request) {
+        return null;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
index 8cb16c8..286c3b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
@@ -91,7 +91,6 @@
         }
     }
 
-    // TODO move from SizeCompatModeActivityController from system UI.
     @Override
     public void onDisplayRemoved(int displayId) {
         mDisplayContextCache.remove(displayId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 3198725..b5e1896 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -220,9 +220,8 @@
         final InputChannel tmpInputChannel = new InputChannel();
         mainExecutor.execute(() -> {
             try {
-                final int res = session.addToDisplay(window, layoutParams, View.GONE,
-                        displayId, mTmpInsetsState, tmpFrames.frame, tmpInputChannel,
-                        mTmpInsetsState, mTempControls);
+                final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
+                        mTmpInsetsState, tmpInputChannel, mTmpInsetsState, mTempControls);
                 if (res < 0) {
                     Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                     return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 8271b06..ac93a17 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -31,7 +31,9 @@
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
 import java.util.ArrayList;
 
@@ -71,14 +73,20 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         IRemoteTransition pendingRemote = mPendingRemotes.remove(transition);
         if (pendingRemote == null) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s doesn't have "
+                    + "explicit remote, search filters for match for %s", transition, info);
             // If no explicit remote, search filters until one matches
             for (int i = mFilters.size() - 1; i >= 0; --i) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Checking filter %s",
+                        mFilters.get(i));
                 if (mFilters.get(i).first.matches(info)) {
                     pendingRemote = mFilters.get(i).second;
                     break;
                 }
             }
         }
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for %s to %s",
+                transition, pendingRemote);
 
         if (pendingRemote == null) return false;
 
@@ -121,6 +129,8 @@
         IRemoteTransition remote = request.getRemoteTransition();
         if (remote == null) return null;
         mPendingRemotes.put(transition, remote);
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested"
+                + " for %s: %s", transition, remote);
         return new WindowContainerTransaction();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 2ab4e0b..d8687bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -232,6 +232,8 @@
         if (!info.getRootLeash().isValid()) {
             // Invalid root-leash implies that the transition is empty/no-op, so just do
             // housekeeping and return.
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s",
+                    transitionToken, info);
             t.apply();
             onFinish(transitionToken, null /* wct */, null /* wctCB */);
             return;
@@ -241,14 +243,22 @@
 
         final TransitionFinishCallback finishCb = (wct, cb) -> onFinish(transitionToken, wct, cb);
         // If a handler chose to uniquely run this animation, try delegating to it.
-        if (active.mFirstHandler != null && active.mFirstHandler.startAnimation(
-                transitionToken, info, t, finishCb)) {
-            return;
+        if (active.mFirstHandler != null) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
+                    active.mFirstHandler);
+            if (active.mFirstHandler.startAnimation(transitionToken, info, t, finishCb)) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
+                return;
+            }
         }
         // Otherwise give every other handler a chance (in order)
         for (int i = mHandlers.size() - 1; i >= 0; --i) {
             if (mHandlers.get(i) == active.mFirstHandler) continue;
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
+                    mHandlers.get(i));
             if (mHandlers.get(i).startAnimation(transitionToken, info, t, finishCb)) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
+                        mHandlers.get(i));
                 return;
             }
         }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 5374bd9..2c29220 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -18,6 +18,7 @@
 
 import android.os.Bundle
 import android.platform.test.annotations.Presubmit
+import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
@@ -47,6 +48,7 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+// @FlakyTest(bugId = 179116910)
 class EnterSplitScreenDockActivity(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
@@ -92,7 +94,9 @@
             }
             return FlickerTestRunnerFactory.getInstance().buildTest(
                 instrumentation, defaultTransitionSetup, testSpec,
-                repetitions = SplitScreenHelper.TEST_REPETITIONS)
+                repetitions = SplitScreenHelper.TEST_REPETITIONS,
+                supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
+            )
         }
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index d750403..903971e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -18,6 +18,7 @@
 
 import android.os.Bundle
 import android.platform.test.annotations.Presubmit
+import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
@@ -96,7 +97,9 @@
             }
             return FlickerTestRunnerFactory.getInstance().buildTest(
                 instrumentation, defaultTransitionSetup, testSpec,
-                repetitions = SplitScreenHelper.TEST_REPETITIONS)
+                repetitions = SplitScreenHelper.TEST_REPETITIONS,
+                supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842
+            )
         }
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index 7782364..4933665 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -18,6 +18,7 @@
 
 import android.os.Bundle
 import android.platform.test.annotations.Presubmit
+import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
@@ -86,7 +87,9 @@
             }
             return FlickerTestRunnerFactory.getInstance().buildTest(
                 instrumentation, defaultTransitionSetup, testSpec,
-                repetitions = SplitScreenHelper.TEST_REPETITIONS)
+                repetitions = SplitScreenHelper.TEST_REPETITIONS,
+                supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842
+            )
         }
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index 59f6aaf..ff3a979 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.legacysplitscreen
 
 import android.os.Bundle
+import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
@@ -87,7 +88,9 @@
             }
             return FlickerTestRunnerFactory.getInstance().buildTest(
                 instrumentation, defaultTransitionSetup, testSpec,
-                repetitions = SplitScreenHelper.TEST_REPETITIONS)
+                repetitions = SplitScreenHelper.TEST_REPETITIONS,
+                supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
+            )
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index c802ffe..7edef93 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -18,6 +18,7 @@
 
 import android.os.Bundle
 import android.platform.test.annotations.Presubmit
+import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
@@ -93,7 +94,9 @@
             }
             return FlickerTestRunnerFactory.getInstance().buildTest(
                 instrumentation, defaultTransitionSetup, testSpec,
-                repetitions = SplitScreenHelper.TEST_REPETITIONS)
+                repetitions = SplitScreenHelper.TEST_REPETITIONS,
+                supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
+            )
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index dca2732..c0ab20d 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -15,7 +15,10 @@
 android_test {
     name: "WMShellUnitTests",
 
-    srcs: ["**/*.java"],
+    srcs: [
+        "**/*.java",
+        "**/*.kt",
+    ],
 
     static_libs: [
         "WindowManager-Shell",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
index 4bd9bed..17ed396 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
@@ -26,7 +26,7 @@
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringForce
 import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
+import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.animation.PhysicsAnimator.EndListener
 import com.android.wm.shell.animation.PhysicsAnimator.UpdateListener
 import com.android.wm.shell.animation.PhysicsAnimatorTestUtils.clearAnimationUpdateFrames
@@ -54,8 +54,7 @@
 @TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
-@Ignore("Blocking presubmits - investigating in b/158697054")
-class PhysicsAnimatorTest : SysuiTestCase() {
+class PhysicsAnimatorTest : ShellTestCase() {
     private lateinit var viewGroup: ViewGroup
     private lateinit var testView: View
     private lateinit var testView2: View
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 4fab9a5..dd1a6a5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -20,12 +20,12 @@
 import android.os.UserHandle
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.systemui.util.mockito.eq
 import com.android.wm.shell.ShellTestCase
 import junit.framework.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
index 495be41..21bc32c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
@@ -27,7 +27,6 @@
 import android.app.IActivityTaskManager;
 import android.content.ComponentName;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
@@ -234,14 +233,6 @@
         verify(mOtherCallback).onActivityRotation(eq(123));
     }
 
-    @Test
-    public void testOnSizeCompatModeActivityChanged() {
-        IBinder b = mock(IBinder.class);
-        mImpl.onSizeCompatModeActivityChanged(123, b);
-        verify(mCallback).onSizeCompatModeActivityChanged(eq(123), eq(b));
-        verify(mOtherCallback).onSizeCompatModeActivityChanged(eq(123), eq(b));
-    }
-
     /**
      * Handler that synchronously calls TaskStackListenerImpl#handleMessage() when it receives a
      * message.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
index fe53641..9f1ee6c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
@@ -21,8 +21,8 @@
 import android.view.View
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.animation.PhysicsAnimatorTestUtils
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.animation.PhysicsAnimatorTestUtils
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -43,7 +43,7 @@
 @TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
-class MagnetizedObjectTest : SysuiTestCase() {
+class MagnetizedObjectTest : ShellTestCase() {
     /** Incrementing value for fake MotionEvent timestamps. */
     private var time = 0L
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index c565a4c..0087d91 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 
+import android.app.TaskInfo;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
@@ -54,6 +55,9 @@
     private SurfaceControl mLeash;
 
     @Mock
+    private TaskInfo mTaskInfo;
+
+    @Mock
     private PipAnimationController.PipAnimationCallback mPipAnimationCallback;
 
     @Before
@@ -70,7 +74,7 @@
     @Test
     public void getAnimator_withAlpha_returnFloatAnimator() {
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
-                .getAnimator(mLeash, new Rect(), 0f, 1f);
+                .getAnimator(mTaskInfo, mLeash, new Rect(), 0f, 1f);
 
         assertEquals("Expect ANIM_TYPE_ALPHA animation",
                 animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA);
@@ -79,7 +83,7 @@
     @Test
     public void getAnimator_withBounds_returnBoundsAnimator() {
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
-                .getAnimator(mLeash, new Rect(), new Rect(), new Rect(), null,
+                .getAnimator(mTaskInfo, mLeash, new Rect(), new Rect(), new Rect(), null,
                         TRANSITION_DIRECTION_TO_PIP, 0);
 
         assertEquals("Expect ANIM_TYPE_BOUNDS animation",
@@ -93,13 +97,13 @@
         final Rect endValue1 = new Rect(100, 100, 200, 200);
         final Rect endValue2 = new Rect(200, 200, 300, 300);
         final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
-                .getAnimator(mLeash, baseValue, startValue, endValue1, null,
+                .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
                         TRANSITION_DIRECTION_TO_PIP, 0);
         oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
         oldAnimator.start();
 
         final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
-                .getAnimator(mLeash, baseValue, startValue, endValue2, null,
+                .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue2, null,
                         TRANSITION_DIRECTION_TO_PIP, 0);
 
         assertEquals("getAnimator with same type returns same animator",
@@ -111,13 +115,13 @@
     @Test
     public void getAnimator_setTransitionDirection() {
         PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
-                .getAnimator(mLeash, new Rect(), 0f, 1f)
+                .getAnimator(mTaskInfo, mLeash, new Rect(), 0f, 1f)
                 .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP);
         assertEquals("Transition to PiP mode",
                 animator.getTransitionDirection(), TRANSITION_DIRECTION_TO_PIP);
 
         animator = mPipAnimationController
-                .getAnimator(mLeash, new Rect(), 0f, 1f)
+                .getAnimator(mTaskInfo, mLeash, new Rect(), 0f, 1f)
                 .setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP);
         assertEquals("Transition to fullscreen mode",
                 animator.getTransitionDirection(), TRANSITION_DIRECTION_LEAVE_PIP);
@@ -131,7 +135,7 @@
         final Rect endValue1 = new Rect(100, 100, 200, 200);
         final Rect endValue2 = new Rect(200, 200, 300, 300);
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
-                .getAnimator(mLeash, baseValue, startValue, endValue1, null,
+                .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
                         TRANSITION_DIRECTION_TO_PIP, 0);
 
         animator.updateEndValue(endValue2);
@@ -145,7 +149,7 @@
         final Rect startValue = new Rect(0, 0, 100, 100);
         final Rect endValue = new Rect(100, 100, 200, 200);
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
-                .getAnimator(mLeash, baseValue, startValue, endValue, null,
+                .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
                         TRANSITION_DIRECTION_TO_PIP, 0);
         animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
 
@@ -153,16 +157,16 @@
 
         // onAnimationStart triggers onPipAnimationStart
         animator.onAnimationStart(animator);
-        verify(mPipAnimationCallback).onPipAnimationStart(animator);
+        verify(mPipAnimationCallback).onPipAnimationStart(mTaskInfo, animator);
 
         // onAnimationCancel triggers onPipAnimationCancel
         animator.onAnimationCancel(animator);
-        verify(mPipAnimationCallback).onPipAnimationCancel(animator);
+        verify(mPipAnimationCallback).onPipAnimationCancel(mTaskInfo, animator);
 
         // onAnimationEnd triggers onPipAnimationEnd
         animator.onAnimationEnd(animator);
-        verify(mPipAnimationCallback).onPipAnimationEnd(any(SurfaceControl.Transaction.class),
-                eq(animator));
+        verify(mPipAnimationCallback).onPipAnimationEnd(eq(mTaskInfo),
+                any(SurfaceControl.Transaction.class), eq(animator));
     }
 
     /**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 7a810a1..9430af9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -18,27 +18,23 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
-import android.graphics.Rect;
 import android.os.RemoteException;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Rational;
 import android.util.Size;
-import android.view.Display;
 import android.view.DisplayInfo;
 import android.window.WindowContainerToken;
 
@@ -47,9 +43,8 @@
 import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.pip.phone.PhonePipMenuController;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.pip.phone.PhonePipMenuController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -69,14 +64,17 @@
     private PipTaskOrganizer mSpiedPipTaskOrganizer;
 
     @Mock private DisplayController mMockdDisplayController;
-    @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
+
     @Mock private PhonePipMenuController mMockPhonePipMenuController;
+    @Mock private PipAnimationController mMockPipAnimationController;
+    @Mock private PipTransitionController mMockPipTransitionController;
     @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
     @Mock private PipUiEventLogger mMockPipUiEventLogger;
     @Mock private Optional<LegacySplitScreen> mMockOptionalSplitScreen;
     @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
     private TestShellExecutor mMainExecutor;
     private PipBoundsState mPipBoundsState;
+    private PipBoundsAlgorithm mPipBoundsAlgorithm;
 
     private ComponentName mComponent1;
     private ComponentName mComponent2;
@@ -87,10 +85,12 @@
         mComponent1 = new ComponentName(mContext, "component1");
         mComponent2 = new ComponentName(mContext, "component2");
         mPipBoundsState = new PipBoundsState(mContext);
+        mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState);
         mMainExecutor = new TestShellExecutor();
         mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState,
-                mMockPipBoundsAlgorithm, mMockPhonePipMenuController,
-                mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController,
+                mPipBoundsAlgorithm, mMockPhonePipMenuController,
+                mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
+                mMockPipTransitionController, mMockOptionalSplitScreen, mMockdDisplayController,
                 mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
         mMainExecutor.flushAll();
         preparePipTaskOrg();
@@ -117,7 +117,7 @@
 
     @Test
     public void startSwipePipToHome_updatesLastPipComponentName() {
-        mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, null);
+        mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(null));
 
         assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName());
     }
@@ -126,7 +126,8 @@
     public void startSwipePipToHome_updatesOverrideMinSize() {
         final Size minSize = new Size(100, 80);
 
-        mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize), null);
+        mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize),
+                createPipParams(null));
 
         assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
     }
@@ -200,9 +201,6 @@
         final DisplayInfo info = new DisplayInfo();
         mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
                 mContext.getResources(), true, true));
-        when(mMockPipBoundsAlgorithm.getEntryDestinationBounds()).thenReturn(new Rect());
-        when(mMockPipBoundsAlgorithm.getAdjustedDestinationBounds(any(), anyFloat()))
-                .thenReturn(new Rect());
         mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
         doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong());
         doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 62ffac4..cfe8463 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -46,6 +46,7 @@
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -68,6 +69,7 @@
     @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
     @Mock private PipMediaController mMockPipMediaController;
     @Mock private PipTaskOrganizer mMockPipTaskOrganizer;
+    @Mock private PipTransitionController mMockPipTransitionController;
     @Mock private PipTouchHandler mMockPipTouchHandler;
     @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
     @Mock private PipBoundsState mMockPipBoundsState;
@@ -80,8 +82,8 @@
         mPipController = new PipController(mContext, mMockDisplayController,
                 mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
                 mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
-                mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener,
-                mMockExecutor);
+                mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
+                mMockTaskStackListener, mMockExecutor);
         doAnswer(invocation -> {
             ((Runnable) invocation.getArgument(0)).run();
             return null;
@@ -90,7 +92,7 @@
 
     @Test
     public void instantiatePipController_registersPipTransitionCallback() {
-        verify(mMockPipTaskOrganizer).registerPipTransitionCallback(any());
+        verify(mMockPipTransitionController).registerPipTransitionCallback(any());
     }
 
     @Test
@@ -113,8 +115,8 @@
         assertNull(PipController.create(spyContext, mMockDisplayController,
                 mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
                 mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
-                mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener,
-                mMockExecutor));
+                mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
+                mMockTaskStackListener, mMockExecutor));
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index b4cfbc2..449ad88 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -38,6 +38,7 @@
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipUiEventLogger;
 
 import org.junit.Before;
@@ -67,6 +68,9 @@
     private PipTaskOrganizer mPipTaskOrganizer;
 
     @Mock
+    private PipTransitionController mMockPipTransitionController;
+
+    @Mock
     private FloatingContentCoordinator mFloatingContentCoordinator;
 
     @Mock
@@ -98,7 +102,8 @@
         mPipSnapAlgorithm = new PipSnapAlgorithm();
         mPipTouchHandler = new PipTouchHandler(mContext, mPhonePipMenuController,
                 mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer,
-                mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor);
+                mMockPipTransitionController, mFloatingContentCoordinator, mPipUiEventLogger,
+                mMainExecutor);
         mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
         mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
         mPipTouchHandler.setPipMotionHelper(mMotionHelper);
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 65f4e8c..e798f2a 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -129,8 +129,9 @@
 
     runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false);
 
-    defaultRenderAhead = std::max(-1, std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD,
-            render_ahead().value_or(0))));
+    defaultRenderAhead = std::max(
+            -1,
+            std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD, render_ahead().value_or(-1))));
 
     return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
 }
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index 37e5276..a07723f 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -83,6 +83,23 @@
     return reinterpret_cast<jlong>(releaseFontFamily);
 }
 
+// FastNative
+static jstring FontFamily_getLangTags(JNIEnv* env, jobject, jlong familyPtr) {
+    FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+    uint32_t localeListId = family->family->localeListId();
+    if (localeListId == 0) {
+        return nullptr;
+    }
+    std::string langTags = minikin::getLocaleString(localeListId);
+    return env->NewStringUTF(langTags.c_str());
+}
+
+// CriticalNative
+static jint FontFamily_getVariant(jlong familyPtr) {
+    FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+    return static_cast<jint>(family->family->variant());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gFontFamilyBuilderMethods[] = {
@@ -93,9 +110,16 @@
     { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
 };
 
+static const JNINativeMethod gFontFamilyMethods[] = {
+        {"nGetLangTags", "(J)Ljava/lang/String;", (void*)FontFamily_getLangTags},
+        {"nGetVariant", "(J)I", (void*)FontFamily_getVariant},
+};
+
 int register_android_graphics_fonts_FontFamily(JNIEnv* env) {
     return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder",
-            gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods));
+                                gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods)) +
+           RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily", gFontFamilyMethods,
+                                NELEM(gFontFamilyMethods));
 }
 
 }
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 633f21c..37a6ee7 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -157,12 +157,14 @@
 void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
     ATRACE_CALL();
 
-    if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
-        mFixedRenderAhead = false;
-        mRenderAheadCapacity = 1;
-    } else {
-        mFixedRenderAhead = true;
+    if (mFixedRenderAhead) {
         mRenderAheadCapacity = mRenderAheadDepth;
+    } else {
+        if (DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
+            mRenderAheadCapacity = 1;
+        } else {
+            mRenderAheadCapacity = 0;
+        }
     }
 
     if (window) {
@@ -764,11 +766,16 @@
 }
 
 void CanvasContext::setRenderAheadDepth(int renderAhead) {
-    if (renderAhead > 2 || renderAhead < 0 || mNativeSurface) {
+    if (renderAhead > 2 || renderAhead < -1 || mNativeSurface) {
         return;
     }
-    mFixedRenderAhead = true;
-    mRenderAheadDepth = static_cast<uint32_t>(renderAhead);
+    if (renderAhead == -1) {
+        mFixedRenderAhead = false;
+        mRenderAheadDepth = 0;
+    } else {
+        mFixedRenderAhead = true;
+        mRenderAheadDepth = static_cast<uint32_t>(renderAhead);
+    }
 }
 
 SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index ce92661..40d8615 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -16,7 +16,7 @@
 
 package android.location;
 
-import android.location.LocationResult;
+import android.location.Location;
 import android.os.IRemoteCallback;
 
 /**
@@ -24,7 +24,7 @@
  */
 oneway interface ILocationListener
 {
-    void onLocationChanged(in LocationResult locationResult, in @nullable IRemoteCallback onCompleteCallback);
+    void onLocationChanged(in List<Location> locations, in @nullable IRemoteCallback onCompleteCallback);
     void onProviderEnabledChanged(String provider, boolean enabled);
     void onFlushComplete(int requestCode);
 }
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 523117b..35a4091 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.os.Bundle;
 
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -48,15 +49,18 @@
     /**
      * Called when the location has changed and locations are being delivered in batches. The
      * default implementation calls through to ({@link #onLocationChanged(Location)} with all
-     * locations in the batch, from earliest to latest.
+     * locations in the batch. The list of locations is always guaranteed to be non-empty, and is
+     * always guaranteed to be ordered from earliest location to latest location (so that the
+     * earliest location in the batch is at index 0 in the list, and the latest location in the
+     * batch is at index size-1 in the list).
      *
      * @see LocationRequest#getMaxUpdateDelayMillis()
-     * @param locationResult the location result list
+     * @param locations the location list
      */
-    default void onLocationChanged(@NonNull LocationResult locationResult) {
-        final int size = locationResult.size();
-        for (int i = 0; i < size; ++i) {
-            onLocationChanged(locationResult.get(i));
+    default void onLocationChanged(@NonNull List<Location> locations) {
+        final int size = locations.size();
+        for (int i = 0; i < size; i++) {
+            onLocationChanged(locations.get(i));
         }
     }
 
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index fdb044d..d569482 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
 import static android.Manifest.permission.LOCATION_HARDWARE;
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static android.location.GpsStatus.GPS_EVENT_STARTED;
 import static android.location.LocationRequest.createFromDeprecatedCriteria;
 import static android.location.LocationRequest.createFromDeprecatedProvider;
 
@@ -43,7 +44,6 @@
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -60,16 +60,17 @@
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.listeners.ListenerExecutor;
-import com.android.internal.listeners.ListenerTransportMultiplexer;
+import com.android.internal.listeners.ListenerTransport;
+import com.android.internal.listeners.ListenerTransportManager;
 import com.android.internal.util.Preconditions;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -231,19 +232,26 @@
 
     /**
      * Key used for an extra holding a {@link Location} value when a location change is sent using
-     * a PendingIntent.
+     * a PendingIntent. If the location change includes a list of batched locations via
+     * {@link #KEY_LOCATIONS} then this key will still be present, and will hold the last location
+     * in the batch. Use {@link Intent#getParcelableExtra(String)} to retrieve the location.
      *
      * @see #requestLocationUpdates(String, LocationRequest, PendingIntent)
      */
     public static final String KEY_LOCATION_CHANGED = "location";
 
     /**
-     * Key used for an extra holding a {@link LocationResult} value when a location change is sent
-     * using a PendingIntent.
+     * Key used for an extra holding a array of {@link Location}s when a location change is sent
+     * using a PendingIntent. This key will only be present if the location change includes
+     * multiple (ie, batched) locations, otherwise only {@link #KEY_LOCATION_CHANGED} will be
+     * present. Use {@link Intent#getParcelableArrayExtra(String)} to retrieve the locations.
+     *
+     * <p>The array of locations will never be empty, and will ordered from earliest location to
+     * latest location, the same as with {@link LocationListener#onLocationChanged(List)}.
      *
      * @see #requestLocationUpdates(String, LocationRequest, PendingIntent)
      */
-    public static final String KEY_LOCATION_RESULT = "locationResult";
+    public static final String KEY_LOCATIONS = "locations";
 
     /**
      * Key used for an extra holding an integer request code when location flush completion is sent
@@ -398,20 +406,40 @@
 
     private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000;
 
+    private static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY =
+            "cache_key.location_enabled";
+
+    private static ILocationManager getService() throws RemoteException {
+        try {
+            return ILocationManager.Stub.asInterface(
+                    ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE));
+        } catch (ServiceManager.ServiceNotFoundException e) {
+            throw new RemoteException(e);
+        }
+    }
+
     @GuardedBy("sLocationListeners")
     private static final WeakHashMap<LocationListener, WeakReference<LocationListenerTransport>>
             sLocationListeners = new WeakHashMap<>();
 
-    final Context mContext;
+    // allows lazy instantiation since most processes do not use GNSS APIs
+    private static class GnssLazyLoader {
+        static final GnssStatusTransportManager sGnssStatusListeners =
+                new GnssStatusTransportManager();
+        static final GnssNmeaTransportManager sGnssNmeaListeners =
+                new GnssNmeaTransportManager();
+        static final GnssMeasurementsTransportManager sGnssMeasurementsListeners =
+                new GnssMeasurementsTransportManager();
+        static final GnssAntennaTransportManager sGnssAntennaInfoListeners =
+                new GnssAntennaTransportManager();
+        static final GnssNavigationTransportManager sGnssNavigationListeners =
+                new GnssNavigationTransportManager();
+    }
 
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
-            + "LocationManager}")
-    final ILocationManager mService;
+    private final Context mContext;
+    private final ILocationManager mService;
 
-    private final Object mLock = new Object();
-
-    @GuardedBy("mLock")
-    private PropertyInvalidatedCache<Integer, Boolean> mLocationEnabledCache =
+    private volatile PropertyInvalidatedCache<Integer, Boolean> mLocationEnabledCache =
             new PropertyInvalidatedCache<Integer, Boolean>(
                     4,
                     CACHE_KEY_LOCATION_ENABLED_PROPERTY) {
@@ -425,68 +453,12 @@
                 }
             };
 
-    @GuardedBy("mLock")
-    @Nullable private GnssStatusTransportMultiplexer mGnssStatusTransportMultiplexer;
-    @GuardedBy("mLock")
-    @Nullable private GnssNmeaTransportMultiplexer mGnssNmeaTransportMultiplexer;
-    @GuardedBy("mLock")
-    @Nullable private GnssMeasurementsTransportMultiplexer mGnssMeasurementsTransportMultiplexer;
-    @GuardedBy("mLock")
-    @Nullable private GnssNavigationTransportMultiplexer mGnssNavigationTransportMultiplexer;
-    @GuardedBy("mLock")
-    @Nullable private GnssAntennaInfoTransportMultiplexer mGnssAntennaInfoTransportMultiplexer;
-
     /**
      * @hide
      */
     public LocationManager(@NonNull Context context, @NonNull ILocationManager service) {
-        mService = service;
-        mContext = context;
-    }
-
-    private GnssStatusTransportMultiplexer getGnssStatusTransportMultiplexer() {
-        synchronized (mLock) {
-            if (mGnssStatusTransportMultiplexer == null) {
-                mGnssStatusTransportMultiplexer = new GnssStatusTransportMultiplexer();
-            }
-            return mGnssStatusTransportMultiplexer;
-        }
-    }
-
-    private GnssNmeaTransportMultiplexer getGnssNmeaTransportMultiplexer() {
-        synchronized (mLock) {
-            if (mGnssNmeaTransportMultiplexer == null) {
-                mGnssNmeaTransportMultiplexer = new GnssNmeaTransportMultiplexer();
-            }
-            return mGnssNmeaTransportMultiplexer;
-        }
-    }
-
-    private GnssMeasurementsTransportMultiplexer getGnssMeasurementsTransportMultiplexer() {
-        synchronized (mLock) {
-            if (mGnssMeasurementsTransportMultiplexer == null) {
-                mGnssMeasurementsTransportMultiplexer = new GnssMeasurementsTransportMultiplexer();
-            }
-            return mGnssMeasurementsTransportMultiplexer;
-        }
-    }
-
-    private GnssNavigationTransportMultiplexer getGnssNavigationTransportMultiplexer() {
-        synchronized (mLock) {
-            if (mGnssNavigationTransportMultiplexer == null) {
-                mGnssNavigationTransportMultiplexer = new GnssNavigationTransportMultiplexer();
-            }
-            return mGnssNavigationTransportMultiplexer;
-        }
-    }
-
-    private GnssAntennaInfoTransportMultiplexer getGnssAntennaInfoTransportMultiplexer() {
-        synchronized (mLock) {
-            if (mGnssAntennaInfoTransportMultiplexer == null) {
-                mGnssAntennaInfoTransportMultiplexer = new GnssAntennaInfoTransportMultiplexer();
-            }
-            return mGnssAntennaInfoTransportMultiplexer;
-        }
+        mContext = Objects.requireNonNull(context);
+        mService = Objects.requireNonNull(service);
     }
 
     /**
@@ -627,10 +599,9 @@
      */
     @SystemApi
     public boolean isLocationEnabledForUser(@NonNull UserHandle userHandle) {
-        synchronized (mLock) {
-            if (mLocationEnabledCache != null) {
-                return mLocationEnabledCache.query(userHandle.getIdentifier());
-            }
+        PropertyInvalidatedCache<Integer, Boolean> cache = mLocationEnabledCache;
+        if (cache != null) {
+            return cache.query(userHandle.getIdentifier());
         }
 
         // fallback if cache is disabled
@@ -1905,6 +1876,7 @@
     @Deprecated
     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
     @Nullable
+    @SuppressWarnings("NullableCollection")
     public List<String> getProviderPackages(@NonNull String provider) {
         try {
             return mService.getProviderPackages(provider);
@@ -2264,9 +2236,8 @@
                     "GpsStatus APIs not supported, please use GnssStatus APIs instead");
         }
 
-        GnssStatusTransportMultiplexer multiplexer = getGnssStatusTransportMultiplexer();
-        GnssStatus gnssStatus = multiplexer.getGnssStatus();
-        int ttff = multiplexer.getTtff();
+        GnssStatus gnssStatus = GpsStatusTransport.sGnssStatus;
+        int ttff = GpsStatusTransport.sTtff;
         if (gnssStatus != null) {
             if (status == null) {
                 status = GpsStatus.create(gnssStatus, ttff);
@@ -2299,8 +2270,8 @@
                     "GpsStatus APIs not supported, please use GnssStatus APIs instead");
         }
 
-        getGnssStatusTransportMultiplexer().addListener(listener,
-                new HandlerExecutor(new Handler()));
+        GnssLazyLoader.sGnssStatusListeners.addListener(listener,
+                new GpsStatusTransport(new HandlerExecutor(new Handler()), mContext, listener));
         return true;
     }
 
@@ -2319,7 +2290,7 @@
                     "GpsStatus APIs not supported, please use GnssStatus APIs instead");
         }
 
-        getGnssStatusTransportMultiplexer().removeListener(listener);
+        GnssLazyLoader.sGnssStatusListeners.removeListener(listener);
     }
 
     /**
@@ -2382,7 +2353,8 @@
     public boolean registerGnssStatusCallback(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull GnssStatus.Callback callback) {
-        getGnssStatusTransportMultiplexer().addListener(callback, executor);
+        GnssLazyLoader.sGnssStatusListeners.addListener(callback,
+                new GnssStatusTransport(executor, mContext, callback));
         return true;
     }
 
@@ -2392,7 +2364,7 @@
      * @param callback GNSS status callback object to remove
      */
     public void unregisterGnssStatusCallback(@NonNull GnssStatus.Callback callback) {
-        getGnssStatusTransportMultiplexer().removeListener(callback);
+        GnssLazyLoader.sGnssStatusListeners.removeListener(callback);
     }
 
     /**
@@ -2472,7 +2444,8 @@
     public boolean addNmeaListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnNmeaMessageListener listener) {
-        getGnssNmeaTransportMultiplexer().addListener(listener, executor);
+        GnssLazyLoader.sGnssNmeaListeners.addListener(listener,
+                new GnssNmeaTransport(executor, mContext, listener));
         return true;
     }
 
@@ -2482,7 +2455,7 @@
      * @param listener a {@link OnNmeaMessageListener} object to remove
      */
     public void removeNmeaListener(@NonNull OnNmeaMessageListener listener) {
-        getGnssNmeaTransportMultiplexer().removeListener(listener);
+        GnssLazyLoader.sGnssNmeaListeners.removeListener(listener);
     }
 
     /**
@@ -2598,10 +2571,8 @@
             @NonNull GnssRequest request,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull GnssMeasurementsEvent.Callback callback) {
-        Preconditions.checkArgument(request != null, "invalid null request");
-        getGnssMeasurementsTransportMultiplexer().addListener(request.toGnssMeasurementRequest(),
-                callback, executor);
-        return true;
+        return registerGnssMeasurementsCallback(request.toGnssMeasurementRequest(), executor,
+                callback);
     }
 
     /**
@@ -2623,8 +2594,8 @@
             @NonNull GnssMeasurementRequest request,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull GnssMeasurementsEvent.Callback callback) {
-        Preconditions.checkArgument(request != null, "invalid null request");
-        getGnssMeasurementsTransportMultiplexer().addListener(request, callback, executor);
+        GnssLazyLoader.sGnssMeasurementsListeners.addListener(callback,
+                new GnssMeasurementsTransport(executor, mContext, request, callback));
         return true;
     }
 
@@ -2656,7 +2627,7 @@
      */
     public void unregisterGnssMeasurementsCallback(
             @NonNull GnssMeasurementsEvent.Callback callback) {
-        getGnssMeasurementsTransportMultiplexer().removeListener(callback);
+        GnssLazyLoader.sGnssMeasurementsListeners.removeListener(callback);
     }
 
     /**
@@ -2681,7 +2652,8 @@
     public boolean registerAntennaInfoListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull GnssAntennaInfo.Listener listener) {
-        getGnssAntennaInfoTransportMultiplexer().addListener(listener, executor);
+        GnssLazyLoader.sGnssAntennaInfoListeners.addListener(listener,
+                new GnssAntennaInfoTransport(executor, mContext, listener));
         return true;
     }
 
@@ -2694,7 +2666,7 @@
      */
     @Deprecated
     public void unregisterAntennaInfoListener(@NonNull GnssAntennaInfo.Listener listener) {
-        getGnssAntennaInfoTransportMultiplexer().removeListener(listener);
+        GnssLazyLoader.sGnssAntennaInfoListeners.removeListener(listener);
     }
 
     /**
@@ -2784,7 +2756,8 @@
     public boolean registerGnssNavigationMessageCallback(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull GnssNavigationMessage.Callback callback) {
-        getGnssNavigationTransportMultiplexer().addListener(callback, executor);
+        GnssLazyLoader.sGnssNavigationListeners.addListener(callback,
+                new GnssNavigationTransport(executor, mContext, callback));
         return true;
     }
 
@@ -2795,7 +2768,7 @@
      */
     public void unregisterGnssNavigationMessageCallback(
             @NonNull GnssNavigationMessage.Callback callback) {
-        getGnssNavigationTransportMultiplexer().removeListener(callback);
+        GnssLazyLoader.sGnssNavigationListeners.removeListener(callback);
     }
 
     /**
@@ -2904,6 +2877,89 @@
         }
     }
 
+    private static class GnssStatusTransportManager extends
+            ListenerTransportManager<GnssStatusTransport> {
+
+        @Override
+        protected void registerTransport(GnssStatusTransport transport)
+                            throws RemoteException {
+            getService().registerGnssStatusCallback(transport, transport.getPackage(),
+                    transport.getAttributionTag());
+        }
+
+        @Override
+        protected void unregisterTransport(GnssStatusTransport transport)
+                            throws RemoteException {
+            getService().unregisterGnssStatusCallback(transport);
+        }
+    }
+
+    private static class GnssNmeaTransportManager extends
+            ListenerTransportManager<GnssNmeaTransport> {
+
+        @Override
+        protected void registerTransport(GnssNmeaTransport transport)
+                            throws RemoteException {
+            getService().registerGnssNmeaCallback(transport, transport.getPackage(),
+                    transport.getAttributionTag());
+        }
+
+        @Override
+        protected void unregisterTransport(GnssNmeaTransport transport)
+                            throws RemoteException {
+            getService().unregisterGnssNmeaCallback(transport);
+        }
+    }
+
+    private static class GnssMeasurementsTransportManager extends
+            ListenerTransportManager<GnssMeasurementsTransport> {
+
+        @Override
+        protected void registerTransport(GnssMeasurementsTransport transport)
+                            throws RemoteException {
+            getService().addGnssMeasurementsListener(transport.getRequest(), transport,
+                    transport.getPackage(), transport.getAttributionTag());
+        }
+
+        @Override
+        protected void unregisterTransport(GnssMeasurementsTransport transport)
+                            throws RemoteException {
+            getService().removeGnssMeasurementsListener(transport);
+        }
+    }
+
+    private static class GnssAntennaTransportManager extends
+            ListenerTransportManager<GnssAntennaInfoTransport> {
+
+        @Override
+        protected void registerTransport(GnssAntennaInfoTransport transport) {
+            transport.getContext().registerReceiver(transport,
+                    new IntentFilter(ACTION_GNSS_ANTENNA_INFOS_CHANGED));
+        }
+
+        @Override
+        protected void unregisterTransport(GnssAntennaInfoTransport transport) {
+            transport.getContext().unregisterReceiver(transport);
+        }
+    }
+
+    private static class GnssNavigationTransportManager extends
+            ListenerTransportManager<GnssNavigationTransport> {
+
+        @Override
+        protected void registerTransport(GnssNavigationTransport transport)
+                            throws RemoteException {
+            getService().addGnssNavigationMessageListener(transport,
+                    transport.getPackage(), transport.getAttributionTag());
+        }
+
+        @Override
+        protected void unregisterTransport(GnssNavigationTransport transport)
+                            throws RemoteException {
+            getService().removeGnssNavigationMessageListener(transport);
+        }
+    }
+
     private static class GetCurrentLocationTransport extends ILocationCallback.Stub implements
             ListenerExecutor, CancellationSignal.OnCancelListener {
 
@@ -2969,12 +3025,12 @@
         }
 
         @Override
-        public void onLocationChanged(LocationResult locationResult,
+        public void onLocationChanged(List<Location> locations,
                 @Nullable IRemoteCallback onCompleteCallback) {
             executeSafely(mExecutor, () -> mListener, new ListenerOperation<LocationListener>() {
                 @Override
                 public void operate(LocationListener listener) {
-                    listener.onLocationChanged(locationResult);
+                    listener.onLocationChanged(locations);
                 }
 
                 @Override
@@ -3018,7 +3074,7 @@
 
         @Override
         public void onStarted() {
-            mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
+            mGpsListener.onGpsStatusChanged(GPS_EVENT_STARTED);
         }
 
         @Override
@@ -3037,273 +3093,269 @@
         }
     }
 
-    private class GnssStatusTransportMultiplexer extends
-            ListenerTransportMultiplexer<Void, GnssStatus.Callback> {
+    private static class GnssStatusTransport extends IGnssStatusListener.Stub implements
+            ListenerTransport<GnssStatus.Callback> {
 
-        private @Nullable IGnssStatusListener mListenerTransport;
+        private final Executor mExecutor;
+        private final String mPackageName;
+        private final String mAttributionTag;
 
-        volatile @Nullable GnssStatus mGnssStatus;
-        volatile int mTtff;
+        private volatile @Nullable GnssStatus.Callback mListener;
 
-        GnssStatusTransportMultiplexer() {}
-
-        public GnssStatus getGnssStatus() {
-            return mGnssStatus;
+        GnssStatusTransport(Executor executor, Context context, GnssStatus.Callback listener) {
+            Preconditions.checkArgument(executor != null, "invalid null executor");
+            Preconditions.checkArgument(listener != null, "invalid null callback");
+            mExecutor = executor;
+            mPackageName = context.getPackageName();
+            mAttributionTag = context.getAttributionTag();
+            mListener = listener;
         }
 
-        public int getTtff() {
-            return mTtff;
+        public String getPackage() {
+            return mPackageName;
         }
 
-        public void addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor) {
-            addListener(listener, null, new GpsAdapter(listener), executor);
+        public String getAttributionTag() {
+            return mAttributionTag;
         }
 
         @Override
-        protected void registerWithServer(Void ignored) throws RemoteException {
-            IGnssStatusListener transport = mListenerTransport;
-            if (transport == null) {
-                transport = new GnssStatusListener();
-            }
-
-            // if a remote exception is thrown the transport should not be set
-            mListenerTransport = null;
-            mService.registerGnssStatusCallback(transport, mContext.getPackageName(),
-                    mContext.getAttributionTag());
-            mListenerTransport = transport;
+        public void unregister() {
+            mListener = null;
         }
 
         @Override
-        protected void unregisterWithServer() throws RemoteException {
-            if (mListenerTransport != null) {
-                IGnssStatusListener transport = mListenerTransport;
-                mListenerTransport = null;
-                mService.unregisterGnssStatusCallback(transport);
-            }
+        public @Nullable GnssStatus.Callback getListener() {
+            return mListener;
         }
 
-        private class GnssStatusListener extends IGnssStatusListener.Stub {
+        @Override
+        public void onGnssStarted() {
+            execute(mExecutor, GnssStatus.Callback::onStarted);
+        }
 
-            GnssStatusListener() {}
+        @Override
+        public void onGnssStopped() {
+            execute(mExecutor, GnssStatus.Callback::onStopped);
+        }
 
-            @Override
-            public void onGnssStarted() {
-                deliverToListeners(GnssStatus.Callback::onStarted);
-            }
+        @Override
+        public void onFirstFix(int ttff) {
+            execute(mExecutor, listener -> listener.onFirstFix(ttff));
 
-            @Override
-            public void onGnssStopped() {
-                deliverToListeners(GnssStatus.Callback::onStopped);
-            }
+        }
 
-            @Override
-            public void onFirstFix(int ttff) {
-                mTtff = ttff;
-                deliverToListeners(callback -> callback.onFirstFix(ttff));
-            }
+        @Override
+        public void onSvStatusChanged(GnssStatus gnssStatus) {
+            execute(mExecutor, listener -> listener.onSatelliteStatusChanged(gnssStatus));
+        }
+    }
 
-            @Override
-            public void onSvStatusChanged(GnssStatus gnssStatus) {
-                mGnssStatus = gnssStatus;
-                deliverToListeners(callback -> callback.onSatelliteStatusChanged(gnssStatus));
+    private static class GpsStatusTransport extends GnssStatusTransport {
+
+        static volatile int sTtff;
+        static volatile GnssStatus sGnssStatus;
+
+        GpsStatusTransport(Executor executor, Context context, GpsStatus.Listener listener) {
+            super(executor, context, new GpsAdapter(listener));
+        }
+
+        @Override
+        public void onFirstFix(int ttff) {
+            sTtff = ttff;
+            super.onFirstFix(ttff);
+        }
+
+        @Override
+        public void onSvStatusChanged(GnssStatus gnssStatus) {
+            sGnssStatus = gnssStatus;
+            super.onSvStatusChanged(gnssStatus);
+        }
+    }
+
+    private static class GnssNmeaTransport extends IGnssNmeaListener.Stub implements
+            ListenerTransport<OnNmeaMessageListener> {
+
+        private final Executor mExecutor;
+        private final String mPackageName;
+        private final String mAttributionTag;
+
+        private volatile @Nullable OnNmeaMessageListener mListener;
+
+        GnssNmeaTransport(Executor executor, Context context, OnNmeaMessageListener listener) {
+            Preconditions.checkArgument(executor != null, "invalid null executor");
+            Preconditions.checkArgument(listener != null, "invalid null listener");
+            mExecutor = executor;
+            mPackageName = context.getPackageName();
+            mAttributionTag = context.getAttributionTag();
+            mListener = listener;
+        }
+
+        public String getPackage() {
+            return mPackageName;
+        }
+
+        public String getAttributionTag() {
+            return mAttributionTag;
+        }
+
+        @Override
+        public void unregister() {
+            mListener = null;
+        }
+
+        @Override
+        public @Nullable OnNmeaMessageListener getListener() {
+            return mListener;
+        }
+
+        @Override
+        public void onNmeaReceived(long timestamp, String nmea) {
+            execute(mExecutor, callback -> callback.onNmeaMessage(nmea, timestamp));
+        }
+    }
+
+    private static class GnssMeasurementsTransport extends IGnssMeasurementsListener.Stub implements
+            ListenerTransport<GnssMeasurementsEvent.Callback> {
+
+        private final Executor mExecutor;
+        private final String mPackageName;
+        private final String mAttributionTag;
+        private final GnssMeasurementRequest mRequest;
+
+        private volatile @Nullable GnssMeasurementsEvent.Callback mListener;
+
+        GnssMeasurementsTransport(Executor executor, Context context,
+                GnssMeasurementRequest request, GnssMeasurementsEvent.Callback listener) {
+            Preconditions.checkArgument(executor != null, "invalid null executor");
+            Preconditions.checkArgument(listener != null, "invalid null callback");
+            Preconditions.checkArgument(request != null, "invalid null request");
+            mExecutor = executor;
+            mPackageName = context.getPackageName();
+            mAttributionTag = context.getAttributionTag();
+            mRequest = request;
+            mListener = listener;
+        }
+
+        public String getPackage() {
+            return mPackageName;
+        }
+
+        public String getAttributionTag() {
+            return mAttributionTag;
+        }
+
+        public GnssMeasurementRequest getRequest() {
+            return mRequest;
+        }
+
+        @Override
+        public void unregister() {
+            mListener = null;
+        }
+
+        @Override
+        public @Nullable GnssMeasurementsEvent.Callback getListener() {
+            return mListener;
+        }
+
+        @Override
+        public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
+            execute(mExecutor, callback -> callback.onGnssMeasurementsReceived(event));
+        }
+
+        @Override
+        public void onStatusChanged(int status) {
+            execute(mExecutor, callback -> callback.onStatusChanged(status));
+        }
+    }
+
+    private static class GnssAntennaInfoTransport extends BroadcastReceiver implements
+            ListenerTransport<GnssAntennaInfo.Listener> {
+
+        private final Executor mExecutor;
+        private final Context mContext;
+
+        private volatile @Nullable GnssAntennaInfo.Listener mListener;
+
+        GnssAntennaInfoTransport(Executor executor, Context context,
+                GnssAntennaInfo.Listener listener) {
+            Preconditions.checkArgument(executor != null, "invalid null executor");
+            Preconditions.checkArgument(listener != null, "invalid null listener");
+            mExecutor = executor;
+            mContext = context;
+            mListener = listener;
+        }
+
+        public Context getContext() {
+            return mContext;
+        }
+
+        @Override
+        public void unregister() {
+            mListener = null;
+        }
+
+        @Override
+        public @Nullable GnssAntennaInfo.Listener getListener() {
+            return mListener;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            ArrayList<GnssAntennaInfo> infos = intent.getParcelableArrayListExtra(
+                    EXTRA_GNSS_ANTENNA_INFOS);
+            if (infos != null) {
+                execute(mExecutor, callback -> callback.onGnssAntennaInfoReceived(infos));
             }
         }
     }
 
-    private class GnssNmeaTransportMultiplexer extends
-            ListenerTransportMultiplexer<Void, OnNmeaMessageListener> {
+    private static class GnssNavigationTransport extends IGnssNavigationMessageListener.Stub
+            implements ListenerTransport<GnssNavigationMessage.Callback> {
 
-        private @Nullable IGnssNmeaListener mListenerTransport;
+        private final Executor mExecutor;
+        private final String mPackageName;
+        private final String mAttributionTag;
 
-        GnssNmeaTransportMultiplexer() {}
+        private volatile @Nullable GnssNavigationMessage.Callback mListener;
 
-        public void addListener(@NonNull OnNmeaMessageListener listener,
-                @NonNull Executor executor) {
-            addListener(listener, null, listener, executor);
+        GnssNavigationTransport(Executor executor, Context context,
+                GnssNavigationMessage.Callback listener) {
+            Preconditions.checkArgument(executor != null, "invalid null executor");
+            Preconditions.checkArgument(listener != null, "invalid null callback");
+            mExecutor = executor;
+            mPackageName = context.getPackageName();
+            mAttributionTag = context.getAttributionTag();
+            mListener = listener;
+        }
+
+        public String getPackage() {
+            return mPackageName;
+        }
+
+        public String getAttributionTag() {
+            return mAttributionTag;
         }
 
         @Override
-        protected void registerWithServer(Void ignored) throws RemoteException {
-            IGnssNmeaListener transport = mListenerTransport;
-            if (transport == null) {
-                transport = new GnssNmeaListener();
-            }
-
-            // if a remote exception is thrown the transport should not be set
-            mListenerTransport = null;
-            mService.registerGnssNmeaCallback(transport, mContext.getPackageName(),
-                    mContext.getAttributionTag());
-            mListenerTransport = transport;
+        public void unregister() {
+            mListener = null;
         }
 
         @Override
-        protected void unregisterWithServer() throws RemoteException {
-            if (mListenerTransport != null) {
-                IGnssNmeaListener transport = mListenerTransport;
-                mListenerTransport = null;
-                mService.unregisterGnssNmeaCallback(transport);
-            }
-        }
-
-        private class GnssNmeaListener extends IGnssNmeaListener.Stub {
-
-            GnssNmeaListener() {}
-
-            @Override
-            public void onNmeaReceived(long timestamp, String nmea) {
-                deliverToListeners(callback -> callback.onNmeaMessage(nmea, timestamp));
-            }
-        }
-    }
-
-    private class GnssMeasurementsTransportMultiplexer extends
-            ListenerTransportMultiplexer<GnssMeasurementRequest, GnssMeasurementsEvent.Callback> {
-
-        private @Nullable IGnssMeasurementsListener mListenerTransport;
-
-        GnssMeasurementsTransportMultiplexer() {}
-
-        @Override
-        protected void registerWithServer(GnssMeasurementRequest request) throws RemoteException {
-            IGnssMeasurementsListener transport = mListenerTransport;
-            if (transport == null) {
-                transport = new GnssMeasurementsListener();
-            }
-
-            // if a remote exception is thrown the transport should not be set
-            mListenerTransport = null;
-            mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(),
-                    mContext.getAttributionTag());
-            mListenerTransport = transport;
+        public @Nullable GnssNavigationMessage.Callback getListener() {
+            return mListener;
         }
 
         @Override
-        protected void unregisterWithServer() throws RemoteException {
-            if (mListenerTransport != null) {
-                IGnssMeasurementsListener transport = mListenerTransport;
-                mListenerTransport = null;
-                mService.removeGnssMeasurementsListener(transport);
-            }
+        public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
+            execute(mExecutor, listener -> listener.onGnssNavigationMessageReceived(event));
         }
 
         @Override
-        protected GnssMeasurementRequest mergeRequests(
-                Collection<GnssMeasurementRequest> requests) {
-            GnssMeasurementRequest.Builder builder = new GnssMeasurementRequest.Builder();
-            for (GnssMeasurementRequest request : requests) {
-                if (request.isFullTracking()) {
-                    builder.setFullTracking(true);
-                }
-                if (request.isCorrelationVectorOutputsEnabled()) {
-                    builder.setCorrelationVectorOutputsEnabled(true);
-                }
-            }
-
-            return builder.build();
-        }
-
-        private class GnssMeasurementsListener extends IGnssMeasurementsListener.Stub {
-
-            GnssMeasurementsListener() {}
-
-            @Override
-            public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
-                deliverToListeners(callback -> callback.onGnssMeasurementsReceived(event));
-            }
-
-            @Override
-            public void onStatusChanged(int status) {
-                deliverToListeners(callback -> callback.onStatusChanged(status));
-            }
-        }
-    }
-
-    private class GnssNavigationTransportMultiplexer extends
-            ListenerTransportMultiplexer<Void, GnssNavigationMessage.Callback> {
-
-        @Nullable
-        private IGnssNavigationMessageListener mListenerTransport;
-
-        GnssNavigationTransportMultiplexer() {}
-
-        @Override
-        protected void registerWithServer(Void ignored) throws RemoteException {
-            IGnssNavigationMessageListener transport = mListenerTransport;
-            if (transport == null) {
-                transport = new GnssNavigationMessageListener();
-            }
-
-            // if a remote exception is thrown the transport should not be set
-            mListenerTransport = null;
-            mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(),
-                    mContext.getAttributionTag());
-            mListenerTransport = transport;
-        }
-
-        @Override
-        protected void unregisterWithServer() throws RemoteException {
-            if (mListenerTransport != null) {
-                IGnssNavigationMessageListener transport = mListenerTransport;
-                mListenerTransport = null;
-                mService.removeGnssNavigationMessageListener(transport);
-            }
-        }
-
-        private class GnssNavigationMessageListener extends IGnssNavigationMessageListener.Stub {
-
-            GnssNavigationMessageListener() {}
-
-            @Override
-            public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
-                deliverToListeners(listener -> listener.onGnssNavigationMessageReceived(event));
-            }
-
-            @Override
-            public void onStatusChanged(int status) {
-                deliverToListeners(listener -> listener.onStatusChanged(status));
-            }
-        }
-    }
-
-    private class GnssAntennaInfoTransportMultiplexer extends
-            ListenerTransportMultiplexer<Void, GnssAntennaInfo.Listener> {
-
-        private @Nullable BroadcastReceiver mListenerTransport;
-
-        GnssAntennaInfoTransportMultiplexer() {}
-
-        @Override
-        protected void registerWithServer(Void ignored) {
-            if (mListenerTransport == null) {
-                // if an exception is thrown the transport should not be set
-                BroadcastReceiver transport = new GnssAntennaInfoReceiver();
-                mContext.registerReceiver(transport,
-                        new IntentFilter(ACTION_GNSS_ANTENNA_INFOS_CHANGED));
-                mListenerTransport = transport;
-            }
-        }
-
-        @Override
-        protected void unregisterWithServer() {
-            if (mListenerTransport != null) {
-                BroadcastReceiver transport = mListenerTransport;
-                mListenerTransport = null;
-                mContext.unregisterReceiver(transport);
-            }
-        }
-
-        private class GnssAntennaInfoReceiver extends BroadcastReceiver {
-
-            GnssAntennaInfoReceiver() {}
-
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                ArrayList<GnssAntennaInfo> infos = intent.getParcelableArrayListExtra(
-                        EXTRA_GNSS_ANTENNA_INFOS);
-                if (infos != null) {
-                    deliverToListeners(callback -> callback.onGnssAntennaInfoReceived(infos));
-                }
-            }
+        public void onStatusChanged(int status) {
+            execute(mExecutor, listener -> listener.onStatusChanged(status));
         }
     }
 
@@ -3321,8 +3373,8 @@
         }
 
         @Override
-        public void onLocationChanged(@NonNull LocationResult locationResult) {
-            mCallback.onLocationBatch(locationResult.asList());
+        public void onLocationChanged(@NonNull List<Location> locations) {
+            mCallback.onLocationBatch(locations);
         }
     }
 
@@ -3336,12 +3388,6 @@
     /**
      * @hide
      */
-    private static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY =
-            "cache_key.location_enabled";
-
-    /**
-     * @hide
-     */
     public static void invalidateLocalLocationEnabledCaches() {
         PropertyInvalidatedCache.invalidateCache(CACHE_KEY_LOCATION_ENABLED_PROPERTY);
     }
@@ -3350,8 +3396,6 @@
      * @hide
      */
     public void disableLocalLocationEnabledCaches() {
-        synchronized (mLock) {
-            mLocationEnabledCache = null;
-        }
+        mLocationEnabledCache = null;
     }
 }
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index 60b251e..26cf018 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -23,7 +23,9 @@
  * Information about the properties of a location provider.
  *
  * @deprecated This class is incapable of representing unknown provider properties and may return
- * incorrect results when the properties are unknown.
+ * incorrect results on the rare occasion when a provider's properties are unknown. Prefer using
+ * {@link LocationManager#getProviderProperties(String)} to retrieve {@link ProviderProperties}
+ * instead.
  */
 @Deprecated
 public class LocationProvider {
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 323e740..cb56ee5 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -605,7 +605,7 @@
      * When available, batching can provide substantial power savings to the device, and clients are
      * encouraged to take advantage where appropriate for the use case.
      *
-     * @see LocationListener#onLocationChanged(LocationResult)
+     * @see LocationListener#onLocationChanged(java.util.List)
      * @return the maximum time by which a location update may be delayed
      */
     public @IntRange(from = 0) long getMaxUpdateDelayMillis() {
diff --git a/location/java/android/location/LocationResult.java b/location/java/android/location/LocationResult.java
index 79a000c..8423000 100644
--- a/location/java/android/location/LocationResult.java
+++ b/location/java/android/location/LocationResult.java
@@ -19,7 +19,6 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -34,21 +33,14 @@
 
 /**
  * A location result representing a list of locations, ordered from earliest to latest.
+ *
+ * @hide
  */
 public final class LocationResult implements Parcelable {
 
     /**
-     * Creates a new LocationResult from the given location.
-     */
-    public static @NonNull LocationResult create(@NonNull Location location) {
-        ArrayList<Location> locations = new ArrayList<>(1);
-        locations.add(new Location(Objects.requireNonNull(location)));
-        return new LocationResult(locations);
-    }
-
-    /**
-     * Creates a new LocationResult from the given locations. Locations must be ordered in the same
-     * order they were derived (earliest to latest).
+     * Creates a new LocationResult from the given locations, making a copy of each location.
+     * Locations must be ordered in the same order they were derived (earliest to latest).
      */
     public static @NonNull LocationResult create(@NonNull List<Location> locations) {
         Preconditions.checkArgument(!locations.isEmpty());
@@ -60,16 +52,40 @@
     }
 
     /**
-     * Creates a new LocationResult that takes ownership of the given location without copying it.
-     * Callers must ensure the given location is never mutated after this method is called.
-     *
-     * @hide
+     * Creates a new LocationResult from the given locations, making a copy of each location.
+     * Locations must be ordered in the same order they were derived (earliest to latest).
      */
-    @SystemApi
-    public static @NonNull LocationResult wrap(@NonNull Location location) {
-        ArrayList<Location> locations = new ArrayList<>(1);
-        locations.add(Objects.requireNonNull(location));
-        return new LocationResult(locations);
+    public static @NonNull LocationResult create(@NonNull Location... locations) {
+        Preconditions.checkArgument(locations.length > 0);
+        ArrayList<Location> locationsCopy = new ArrayList<>(locations.length);
+        for (Location location : locations) {
+            locationsCopy.add(new Location(Objects.requireNonNull(location)));
+        }
+        return new LocationResult(locationsCopy);
+    }
+
+    /**
+     * Creates a new LocationResult that takes ownership of the given locations without copying
+     * them. Callers must ensure the given locations are never mutated after this method is called.
+     * Locations must be ordered in the same order they were derived (earliest to latest).
+     */
+    public static @NonNull LocationResult wrap(@NonNull List<Location> locations) {
+        Preconditions.checkArgument(!locations.isEmpty());
+        return new LocationResult(new ArrayList<>(locations));
+    }
+
+    /**
+     * Creates a new LocationResult that takes ownership of the given locations without copying
+     * them. Callers must ensure the given locations are never mutated after this method is called.
+     * Locations must be ordered in the same order they were derived (earliest to latest).
+     */
+    public static @NonNull LocationResult wrap(@NonNull Location... locations) {
+        Preconditions.checkArgument(locations.length > 0);
+        ArrayList<Location> newLocations = new ArrayList<>(locations.length);
+        for (Location location : locations) {
+            newLocations.add(Objects.requireNonNull(location));
+        }
+        return new LocationResult(newLocations);
     }
 
     private final ArrayList<Location> mLocations;
@@ -112,7 +128,7 @@
     }
 
     /**
-     * Returns the numer of locations in this location result.
+     * Returns the number of locations in this location result.
      */
     public @IntRange(from = 1) int size() {
         return mLocations.size();
@@ -139,9 +155,9 @@
      * @hide
      */
     public @NonNull LocationResult deepCopy() {
-        ArrayList<Location> copy = new ArrayList<>(mLocations.size());
         final int size = mLocations.size();
-        for (int i = 0; i < size; ++i) {
+        ArrayList<Location> copy = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
             copy.add(new Location(mLocations.get(i)));
         }
         return new LocationResult(copy);
@@ -164,7 +180,7 @@
      * Returns a LocationResult with only locations that pass the given predicate. This
      * implementation will avoid allocations when no locations are filtered out. The predicate is
      * guaranteed to be invoked once per location, in order from earliest to latest. If all
-     * locations are filtered out a null value is returned instead of an empty LocationResult.
+     * locations are filtered out a null value is returned.
      *
      * @hide
      */
diff --git a/location/java/android/location/provider/ILocationProviderManager.aidl b/location/java/android/location/provider/ILocationProviderManager.aidl
index e3f51d9..50ed046 100644
--- a/location/java/android/location/provider/ILocationProviderManager.aidl
+++ b/location/java/android/location/provider/ILocationProviderManager.aidl
@@ -16,7 +16,7 @@
 
 package android.location.provider;
 
-import android.location.LocationResult;
+import android.location.Location;
 import android.location.provider.ProviderProperties;
 
 /**
@@ -28,6 +28,7 @@
     void onSetAllowed(boolean allowed);
     void onSetProperties(in ProviderProperties properties);
 
-    void onReportLocation(in LocationResult locationResult);
+    void onReportLocation(in Location location);
+    void onReportLocations(in List<Location> locations);
     void onFlushComplete();
 }
diff --git a/location/java/android/location/provider/LocationProviderBase.java b/location/java/android/location/provider/LocationProviderBase.java
index 8f455cd..ae6395d 100644
--- a/location/java/android/location/provider/LocationProviderBase.java
+++ b/location/java/android/location/provider/LocationProviderBase.java
@@ -25,12 +25,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.location.Location;
-import android.location.LocationResult;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -200,35 +201,29 @@
      * Reports a new location from this provider.
      */
     public void reportLocation(@NonNull Location location) {
-        reportLocation(LocationResult.create(location));
+        ILocationProviderManager manager = mManager;
+        if (manager != null) {
+            try {
+                manager.onReportLocation(stripExtras(location));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            } catch (RuntimeException e) {
+                Log.w(mTag, e);
+            }
+        }
     }
 
     /**
-     * Reports a new location result from this provider.
-     *
-     * <p>May only be used from Android S onwards.
+     * Reports a new batch of locations from this provider. Locations must be ordered in the list
+     * from earliest first to latest last.
      */
-    public void reportLocation(@NonNull LocationResult locationResult) {
+    public void reportLocations(@NonNull List<Location> locations) {
         ILocationProviderManager manager = mManager;
         if (manager != null) {
-            locationResult = locationResult.map(location -> {
-                // remove deprecated extras to save on serialization costs
-                Bundle extras = location.getExtras();
-                if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
-                        || extras.containsKey("coarseLocation"))) {
-                    location = new Location(location);
-                    extras = location.getExtras();
-                    extras.remove(EXTRA_NO_GPS_LOCATION);
-                    extras.remove("coarseLocation");
-                    if (extras.isEmpty()) {
-                        location.setExtras(null);
-                    }
-                }
-                return location;
-            });
+
 
             try {
-                manager.onReportLocation(locationResult);
+                manager.onReportLocations(stripExtras(locations));
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             } catch (RuntimeException e) {
@@ -246,9 +241,9 @@
 
     /**
      * Requests a flush of any pending batched locations. The callback must always be invoked once
-     * per invocation, and should be invoked after {@link #reportLocation(LocationResult)} has been
-     * invoked with any flushed locations. The callback may be invoked immediately if no locations
-     * are flushed.
+     * per invocation, and should be invoked after {@link #reportLocation(Location)} or
+     * {@link #reportLocations(List)} has been invoked with any flushed locations. The callback may
+     * be invoked immediately if no locations are flushed.
      */
     public abstract void onFlush(@NonNull OnFlushCompleteCallback callback);
 
@@ -259,6 +254,49 @@
             @SuppressLint("NullableCollection")
             @Nullable Bundle extras);
 
+    private static Location stripExtras(Location location) {
+        Bundle extras = location.getExtras();
+        if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
+                || extras.containsKey("indoorProbability")
+                || extras.containsKey("coarseLocation"))) {
+            location = new Location(location);
+            extras = location.getExtras();
+            extras.remove(EXTRA_NO_GPS_LOCATION);
+            extras.remove("indoorProbability");
+            extras.remove("coarseLocation");
+            if (extras.isEmpty()) {
+                location.setExtras(null);
+            }
+        }
+        return location;
+    }
+
+    private static List<Location> stripExtras(List<Location> locations) {
+        List<Location> mapped = locations;
+        final int size = locations.size();
+        int i = 0;
+        for (Location location : locations) {
+            Location newLocation = stripExtras(location);
+            if (mapped != locations) {
+                mapped.add(newLocation);
+            } else if (newLocation != location) {
+                mapped = new ArrayList<>(size);
+                int j = 0;
+                for (Location copiedLocation : locations) {
+                    if (j >= i) {
+                        break;
+                    }
+                    mapped.add(copiedLocation);
+                    j++;
+                }
+                mapped.add(newLocation);
+            }
+            i++;
+        }
+
+        return mapped;
+    }
+
     private final class Service extends ILocationProvider.Stub {
 
         Service() {}
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 338d7cc..0d81f36 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -21,8 +21,8 @@
     method @Deprecated protected void onInit();
     method @Deprecated protected boolean onSendExtraCommand(@Nullable String, @Nullable android.os.Bundle);
     method @Deprecated protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
-    method @Deprecated public void reportLocation(android.location.Location);
-    method @Deprecated public void reportLocation(android.location.LocationResult);
+    method @Deprecated public void reportLocation(@NonNull android.location.Location);
+    method @Deprecated public void reportLocations(@NonNull java.util.List<android.location.Location>);
     method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setAdditionalProviderPackages(java.util.List<java.lang.String>);
     method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.R) public void setAllowed(boolean);
     method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setEnabled(boolean);
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index aea93ce..7f1cf6d 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,13 +16,13 @@
 
 package com.android.location.provider;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.location.ILocationManager;
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationProvider;
-import android.location.LocationResult;
 import android.location.provider.ILocationProvider;
 import android.location.provider.ILocationProviderManager;
 import android.location.provider.ProviderProperties;
@@ -39,6 +39,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -94,10 +95,6 @@
      */
     public static final String FUSED_PROVIDER = LocationManager.FUSED_PROVIDER;
 
-    private static final String EXTRA_KEY_COARSE_LOCATION = "coarseLocation";
-    private static final String EXTRA_KEY_NO_GPS_LOCATION = "noGPSLocation";
-    private static final String EXTRA_KEY_INDOOR_PROB = "indoorProbability";
-
     final String mTag;
     @Nullable final String mPackageName;
     @Nullable final String mAttributionTag;
@@ -254,20 +251,11 @@
     /**
      * Reports a new location from this provider.
      */
-    public void reportLocation(Location location) {
-        reportLocation(LocationResult.create(location));
-    }
-
-    /**
-     * Reports a new location from this provider.
-     */
-    public void reportLocation(LocationResult locationResult) {
+    public void reportLocation(@NonNull Location location) {
         ILocationProviderManager manager = mManager;
         if (manager != null) {
-            locationResult = locationResult.map(this::cleanUpExtras);
-
             try {
-                manager.onReportLocation(locationResult);
+                manager.onReportLocation(stripExtras(location));
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             } catch (RuntimeException e) {
@@ -277,30 +265,20 @@
     }
 
     /**
-     * Remove deprecated/unnecessary extras to save on serialization costs.
-     *
-     * {@link #EXTRA_KEY_NO_GPS_LOCATION} and {@link #EXTRA_KEY_COARSE_LOCATION} are deprecated.
-     *
-     * {@link #EXTRA_KEY_INDOOR_PROB} should only be used in the framework.
+     * Reports a new batch of locations from this provider. Locations must be ordered in the list
+     * from earliest first to latest last.
      */
-    private Location cleanUpExtras(Location location) {
-        Bundle extras = location.getExtras();
-        if (extras == null) {
-            return location;
-        }
-        if (extras.containsKey(EXTRA_KEY_NO_GPS_LOCATION)
-                || extras.containsKey(EXTRA_KEY_COARSE_LOCATION)
-                || extras.containsKey(EXTRA_KEY_INDOOR_PROB)) {
-            location = new Location(location);
-            extras = location.getExtras();
-            extras.remove(EXTRA_KEY_NO_GPS_LOCATION);
-            extras.remove(EXTRA_KEY_COARSE_LOCATION);
-            extras.remove(EXTRA_KEY_INDOOR_PROB);
-            if (extras.isEmpty()) {
-                location.setExtras(null);
+    public void reportLocations(@NonNull List<Location> locations) {
+        ILocationProviderManager manager = mManager;
+        if (manager != null) {
+            try {
+                manager.onReportLocations(stripExtras(locations));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            } catch (RuntimeException e) {
+                Log.w(mTag, e);
             }
         }
-        return location;
     }
 
     protected void onInit() {
@@ -336,9 +314,9 @@
 
     /**
      * Requests a flush of any pending batched locations. The callback must always be invoked once
-     * per invocation, and should be invoked after {@link #reportLocation(LocationResult)} has been
-     * invoked with any flushed locations. The callback may be invoked immediately if no locations
-     * are flushed.
+     * per invocation, and should be invoked after {@link #reportLocation(Location)} or
+     * {@link #reportLocations(List)} has been invoked with any flushed locations. The callback may
+     * be invoked immediately if no locations are flushed.
      */
     protected void onFlush(OnFlushCompleteCallback callback) {
         callback.onFlushComplete();
@@ -433,4 +411,47 @@
             onSendExtraCommand(command, extras);
         }
     }
+
+    private static Location stripExtras(Location location) {
+        Bundle extras = location.getExtras();
+        if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
+                || extras.containsKey("indoorProbability")
+                || extras.containsKey("coarseLocation"))) {
+            location = new Location(location);
+            extras = location.getExtras();
+            extras.remove(EXTRA_NO_GPS_LOCATION);
+            extras.remove("indoorProbability");
+            extras.remove("coarseLocation");
+            if (extras.isEmpty()) {
+                location.setExtras(null);
+            }
+        }
+        return location;
+    }
+
+    private static List<Location> stripExtras(List<Location> locations) {
+        List<Location> mapped = locations;
+        final int size = locations.size();
+        int i = 0;
+        for (Location location : locations) {
+            Location newLocation = stripExtras(location);
+            if (mapped != locations) {
+                mapped.add(newLocation);
+            } else if (newLocation != location) {
+                mapped = new ArrayList<>(size);
+                int j = 0;
+                for (Location copiedLocation : locations) {
+                    if (j >= i) {
+                        break;
+                    }
+                    mapped.add(copiedLocation);
+                    j++;
+                }
+                mapped.add(newLocation);
+            }
+            i++;
+        }
+
+        return mapped;
+    }
 }
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index c2ce7d3..b196437 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.ActivityThread;
@@ -67,7 +68,14 @@
  * fill with the new audio data. The size of this buffer, specified during the construction,
  * determines how long an AudioRecord can record before "over-running" data that has not
  * been read yet. Data should be read from the audio hardware in chunks of sizes inferior to
- * the total recording buffer size.
+ * the total recording buffer size.</p>
+ * <p>
+ * Applications creating an AudioRecord instance need
+ * {@link android.Manifest.permission#RECORD_AUDIO} or the Builder will throw
+ * {@link java.lang.UnsupportedOperationException} on
+ * {@link android.media.AudioRecord.Builder#build build()},
+ * and the constructor will return an instance in state
+ * {@link #STATE_UNINITIALIZED}.</p>
  */
 public class AudioRecord implements AudioRouting, MicrophoneDirection,
         AudioRecordingMonitor, AudioRecordingMonitorClient
@@ -297,6 +305,7 @@
      *   smaller than getMinBufferSize() will result in an initialization failure.
      * @throws java.lang.IllegalArgumentException
      */
+    @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
     public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
             int bufferSizeInBytes)
     throws IllegalArgumentException {
@@ -334,6 +343,7 @@
      * @throws IllegalArgumentException
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
     public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
             int sessionId) throws IllegalArgumentException {
         mRecordingState = RECORDSTATE_STOPPED;
@@ -718,6 +728,7 @@
          *     were incompatible, or if they are not supported by the device,
          *     or if the device was not available.
          */
+        @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
         public AudioRecord build() throws UnsupportedOperationException {
             if (mAudioPlaybackCaptureConfiguration != null) {
                 return buildAudioPlaybackCaptureRecord();
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index d248f61..7837d7e 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -45,6 +45,7 @@
             case ImageFormat.YV12:
             case ImageFormat.YUV_420_888:
             case ImageFormat.NV21:
+            case ImageFormat.YCBCR_P010:
                 return 3;
             case ImageFormat.NV16:
                 return 2;
@@ -225,6 +226,7 @@
             case ImageFormat.RAW_SENSOR:
             case ImageFormat.RAW_PRIVATE: // round estimate, real size is unknown
             case ImageFormat.DEPTH16:
+            case ImageFormat.YCBCR_P010:
                 estimatedBytePerPixel = 2.0;
                 break;
             case PixelFormat.RGB_888:
@@ -244,6 +246,7 @@
 
     private static Size getEffectivePlaneSizeForImage(Image image, int planeIdx) {
         switch (image.getFormat()) {
+            case ImageFormat.YCBCR_P010:
             case ImageFormat.YV12:
             case ImageFormat.YUV_420_888:
             case ImageFormat.NV21:
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index b6c47fca..ecd9cc1 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -69,6 +69,7 @@
         case HAL_PIXEL_FORMAT_RAW_OPAQUE:
         case HAL_PIXEL_FORMAT_BLOB:
         case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+        case HAL_PIXEL_FORMAT_YCBCR_P010:
             return false;
 
         case HAL_PIXEL_FORMAT_YV12:
@@ -261,6 +262,32 @@
             pStride = 1;
             rStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
             break;
+        case HAL_PIXEL_FORMAT_YCBCR_P010:
+            if (buffer->height % 2 != 0) {
+                ALOGE("YCBCR_P010: height (%d) should be a multiple of 2", buffer->height);
+                return BAD_VALUE;
+            }
+
+            if (buffer->width <= 0) {
+                ALOGE("YCBCR_P010: width (%d) should be a > 0", buffer->width);
+                return BAD_VALUE;
+            }
+
+            if (buffer->height <= 0) {
+                ALOGE("YCBCR_P010: height (%d) should be a > 0", buffer->height);
+                return BAD_VALUE;
+            }
+
+            ySize = (buffer->stride * 2) * buffer->height;
+            cSize = ySize / 2;
+            pStride = (idx == 0) ? 2 : 4;
+            cb = buffer->data + ySize;
+            cr = cb + 2;
+
+            pData = (idx == 0) ?  buffer->data : (idx == 1) ?  cb : cr;
+            dataSize = (idx == 0) ? ySize : cSize;
+            rStride = buffer->stride * 2;
+            break;
         case HAL_PIXEL_FORMAT_Y8:
             // Single plane, 8bpp.
             LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 9ec84d9..eee9f1e 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -3786,7 +3786,7 @@
         jniThrowRuntimeException(env, "Failed to GetByteArrayElements");
         return -1;
     }
-    int realReadSize = filterClient->read(reinterpret_cast<uint8_t*>(dst) + offset, size);
+    int realReadSize = filterClient->read(reinterpret_cast<int8_t*>(dst) + offset, size);
     env->ReleaseByteArrayElements(buffer, dst, 0);
     return (jint) realReadSize;
 }
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index 1a2f8c0..748d458 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -88,12 +88,19 @@
 }
 
 sp<TimeFilterClient> DemuxClient::openTimeFilter() {
-    // TODO: pending aidl interface
+    if (mTunerDemux != NULL) {
+        shared_ptr<ITunerTimeFilter> tunerTimeFilter;
+        Status s = mTunerDemux->openTimeFilter(&tunerTimeFilter);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return NULL;
+        }
+        return new TimeFilterClient(tunerTimeFilter);
+    }
 
     if (mDemux != NULL) {
         sp<ITimeFilter> hidlTimeFilter = openHidlTimeFilter();
         if (hidlTimeFilter != NULL) {
-            sp<TimeFilterClient> timeFilterClient = new TimeFilterClient();
+            sp<TimeFilterClient> timeFilterClient = new TimeFilterClient(NULL);
             timeFilterClient->setHidlTimeFilter(hidlTimeFilter);
             return timeFilterClient;
         }
@@ -103,7 +110,14 @@
 }
 
 int DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) {
-    // pending aidl interface
+    if (mTunerDemux != NULL) {
+        int hwId;
+        Status s = mTunerDemux->getAvSyncHwId(filterClient->getAidlFilter(), &hwId);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return INVALID_AV_SYNC_HW_ID;
+        }
+        return hwId;
+    }
 
     if (mDemux != NULL) {
         uint32_t avSyncHwId;
@@ -119,11 +133,18 @@
         }
     }
 
-    return -1;
+    return INVALID_AV_SYNC_HW_ID;
 }
 
 long DemuxClient::getAvSyncTime(int avSyncHwId) {
-    // pending aidl interface
+    if (mTunerDemux != NULL) {
+        int64_t time;
+        Status s = mTunerDemux->getAvSyncTime(avSyncHwId, &time);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return INVALID_AV_SYNC_TIME;
+        }
+        return time;
+    }
 
     if (mDemux != NULL) {
         uint64_t time;
@@ -138,7 +159,7 @@
         }
     }
 
-    return -1;
+    return INVALID_AV_SYNC_TIME;
 }
 
 sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb) {
@@ -167,7 +188,10 @@
 }
 
 Result DemuxClient::connectCiCam(int ciCamId) {
-    // pending aidl interface
+    if (mTunerDemux != NULL) {
+        Status s = mTunerDemux->connectCiCam(ciCamId);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDemux != NULL) {
         return mDemux->connectCiCam(static_cast<uint32_t>(ciCamId));
@@ -177,7 +201,10 @@
 }
 
 Result DemuxClient::disconnectCiCam() {
-    // pending aidl interface
+    if (mTunerDemux != NULL) {
+        Status s = mTunerDemux->disconnectCiCam();
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDemux != NULL) {
         return mDemux->disconnectCiCam();
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index 463944a..31eb35a 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -31,7 +31,9 @@
 
 using Status = ::ndk::ScopedAStatus;
 using ::aidl::android::media::tv::tuner::ITunerDemux;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
 
+using ::android::hardware::tv::tuner::V1_0::IDemux;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
 using ::android::hardware::tv::tuner::V1_0::DvrType;
 using ::android::hardware::tv::tuner::V1_0::IDemux;
@@ -39,6 +41,9 @@
 
 using namespace std;
 
+const int64_t INVALID_AV_SYNC_TIME = -1;
+const int INVALID_AV_SYNC_HW_ID = -1;
+
 namespace android {
 
 struct DemuxClient : public RefBase {
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 6b78817..8b4ca37 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -18,6 +18,7 @@
 
 #include <aidlcommonsupport/NativeHandle.h>
 #include <android-base/logging.h>
+#include <fmq/ConvertMQDescriptors.h>
 #include <utils/Log.h>
 
 #include "FilterClient.h"
@@ -34,7 +35,7 @@
 using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo;
 using ::aidl::android::media::tv::tuner::TunerFilterTlvConfiguration;
 using ::aidl::android::media::tv::tuner::TunerFilterTsConfiguration;
-
+using ::android::hardware::hidl_vec;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
 using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
 using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
@@ -68,18 +69,12 @@
     mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter);
 }
 
-int FilterClient::read(uint8_t* buffer, int size) {
-    // TODO: pending aidl interface
-
-    if (mFilter != NULL) {
-        Result res = getFilterMq();
-        if (res != Result::SUCCESS) {
-            return -1;
-        }
-        return copyData(buffer, size);
+int FilterClient::read(int8_t* buffer, int size) {
+    Result res = getFilterMq();
+    if (res != Result::SUCCESS) {
+        return -1;
     }
-
-    return -1;
+    return copyData(buffer, size);
 }
 
 SharedHandleInfo FilterClient::getAvSharedHandleInfo() {
@@ -106,7 +101,10 @@
 }
 
 Result FilterClient::configureMonitorEvent(int monitorEventType) {
-    // TODO: pending aidl interface
+    if (mTunerFilter != NULL) {
+        Status s = mTunerFilter->configureMonitorEvent(monitorEventType);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mFilter_1_1 != NULL) {
         return mFilter_1_1->configureMonitorEvent(monitorEventType);
@@ -116,7 +114,10 @@
 }
 
 Result FilterClient::configureIpFilterContextId(int cid) {
-    // TODO: pending aidl interface
+    if (mTunerFilter != NULL) {
+        Status s = mTunerFilter->configureIpFilterContextId(cid);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mFilter_1_1 != NULL) {
         return mFilter_1_1->configureIpCid(cid);
@@ -126,7 +127,19 @@
 }
 
 Result FilterClient::configureAvStreamType(AvStreamType avStreamType) {
-    // TODO: pending aidl interface
+    if (mTunerFilter != NULL) {
+        int type;
+        switch (avStreamType.getDiscriminator()) {
+            case AvStreamType::hidl_discriminator::audio:
+                type = (int)avStreamType.audio();
+                break;
+            case AvStreamType::hidl_discriminator::video:
+                type = (int)avStreamType.video();
+                break;
+        }
+        Status s = mTunerFilter->configureAvStreamType(type);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mFilter_1_1 != NULL) {
         return mFilter_1_1->configureAvStreamType(avStreamType);
@@ -228,7 +241,10 @@
 }
 
 Result FilterClient::setDataSource(sp<FilterClient> filterClient){
-    // TODO: pending aidl interface
+    if (mTunerFilter != NULL) {
+        Status s = mTunerFilter->setDataSource(filterClient->getAidlFilter());
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mFilter != NULL) {
         sp<IFilter> sourceFilter = filterClient->getHalFilter();
@@ -687,10 +703,10 @@
 
 void TunerFilterCallback::getHidlMediaEvent(
         const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         hidl_handle handle = hidl_handle(makeFromAidl(filterEvents[i]
                 .get<TunerFilterEvent::media>().avMemory));
-        event.events.resize(i + 1);
         event.events[i].media({
             .avMemory = handle,
             .streamId = static_cast<DemuxStreamId>(filterEvents[i]
@@ -736,9 +752,9 @@
 
 void TunerFilterCallback::getHidlSectionEvent(
         const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto section = filterEvents[i].get<TunerFilterEvent::section>();
-        event.events.resize(i + 1);
         event.events[i].section({
             .tableId = static_cast<uint16_t>(section.tableId),
             .version = static_cast<uint16_t>(section.version),
@@ -750,9 +766,9 @@
 
 void TunerFilterCallback::getHidlPesEvent(
         const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto pes = filterEvents[i].get<TunerFilterEvent::pes>();
-        event.events.resize(i + 1);
         event.events[i].pes({
             .streamId = static_cast<DemuxStreamId>(pes.streamId),
             .dataLength = static_cast<uint16_t>(pes.dataLength),
@@ -763,9 +779,10 @@
 
 void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents,
         DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+    event.events.resize(filterEvents.size());
+    eventExt.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto ts = filterEvents[i].get<TunerFilterEvent::tsRecord>();
-        event.events.resize(i + 1);
         event.events[i].tsRecord({
             .tsIndexMask = static_cast<uint32_t>(ts.tsIndexMask),
             .byteNumber = static_cast<uint64_t>(ts.byteNumber),
@@ -787,7 +804,6 @@
                 break;
         }
 
-        eventExt.events.resize(i + 1);
         if (ts.isExtended) {
             eventExt.events[i].tsRecord({
                 .pts = static_cast<uint64_t>(ts.pts),
@@ -801,15 +817,15 @@
 
 void TunerFilterCallback::getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents,
         DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+    event.events.resize(filterEvents.size());
+    eventExt.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto mmtp = filterEvents[i].get<TunerFilterEvent::mmtpRecord>();
-        event.events.resize(i + 1);
         event.events[i].mmtpRecord({
             .scHevcIndexMask = static_cast<uint32_t>(mmtp.scHevcIndexMask),
             .byteNumber = static_cast<uint64_t>(mmtp.byteNumber),
         });
 
-        eventExt.events.resize(i + 1);
         if (mmtp.isExtended) {
             eventExt.events[i].mmtpRecord({
                 .pts = static_cast<uint64_t>(mmtp.pts),
@@ -825,9 +841,9 @@
 
 void TunerFilterCallback::getHidlDownloadEvent(const vector<TunerFilterEvent>& filterEvents,
         DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto download = filterEvents[i].get<TunerFilterEvent::download>();
-        event.events.resize(i + 1);
         event.events[i].download({
             .itemId = static_cast<uint32_t>(download.itemId),
             .mpuSequenceNumber = static_cast<uint32_t>(download.mpuSequenceNumber),
@@ -840,9 +856,9 @@
 
 void TunerFilterCallback::getHidlIpPayloadEvent(const vector<TunerFilterEvent>& filterEvents,
         DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto ip = filterEvents[i].get<TunerFilterEvent::ipPayload>();
-        event.events.resize(i + 1);
         event.events[i].ipPayload({
             .dataLength = static_cast<uint16_t>(ip.dataLength),
         });
@@ -851,15 +867,15 @@
 
 void TunerFilterCallback::getHidlTemiEvent(const vector<TunerFilterEvent>& filterEvents,
         DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto temi = filterEvents[i].get<TunerFilterEvent::temi>();
-        event.events.resize(i + 1);
         event.events[i].temi({
             .pts = static_cast<uint64_t>(temi.pts),
             .descrTag = static_cast<uint8_t>(temi.descrTag),
         });
-        vector<uint8_t> descrData(temi.descrData.size());
-        copy(temi.descrData.begin(), temi.descrData.end(), descrData.begin());
+        hidl_vec<uint8_t> descrData(temi.descrData.begin(), temi.descrData.end());
+        event.events[i].temi().descrData = descrData;
     }
 }
 
@@ -891,29 +907,43 @@
 }
 
 Result FilterClient::getFilterMq() {
-    if (mFilter == NULL) {
-        return Result::INVALID_STATE;
-    }
-
     if (mFilterMQ != NULL) {
         return Result::SUCCESS;
     }
 
-    Result getQueueDescResult = Result::UNKNOWN_ERROR;
-    MQDescriptorSync<uint8_t> filterMQDesc;
-    mFilter->getQueueDesc(
-            [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
-                filterMQDesc = desc;
-                getQueueDescResult = r;
-            });
-    if (getQueueDescResult == Result::SUCCESS) {
-        mFilterMQ = std::make_unique<MQ>(filterMQDesc, true);
-        EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+    AidlMQDesc aidlMqDesc;
+    Result res = Result::UNAVAILABLE;
+
+    if (mTunerFilter != NULL) {
+        Status s = mTunerFilter->getQueueDesc(&aidlMqDesc);
+        res = ClientHelper::getServiceSpecificErrorCode(s);
+        if (res == Result::SUCCESS) {
+            mFilterMQ = new (nothrow) AidlMQ(aidlMqDesc);
+            EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+        }
+        return res;
     }
-    return getQueueDescResult;
+
+    if (mFilter != NULL) {
+        MQDescriptorSync<uint8_t> filterMQDesc;
+        mFilter->getQueueDesc(
+                [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
+                    filterMQDesc = desc;
+                    res = r;
+                });
+        if (res == Result::SUCCESS) {
+            AidlMQDesc aidlMQDesc;
+            unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
+                    filterMQDesc,  &aidlMQDesc);
+            mFilterMQ = new (nothrow) AidlMessageQueue(aidlMQDesc);
+            EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+        }
+    }
+
+    return res;
 }
 
-int FilterClient::copyData(uint8_t* buffer, int size) {
+int FilterClient::copyData(int8_t* buffer, int size) {
     if (mFilter == NULL || mFilterMQ == NULL || mFilterMQEventFlag == NULL) {
         return -1;
     }
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index 21919ac..bbabc28 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -25,12 +25,14 @@
 #include <android/hardware/tv/tuner/1.1/IFilter.h>
 #include <android/hardware/tv/tuner/1.1/IFilterCallback.h>
 #include <android/hardware/tv/tuner/1.1/types.h>
+#include <fmq/AidlMessageQueue.h>
 #include <fmq/MessageQueue.h>
 
 #include "ClientHelper.h"
 #include "FilterClientCallback.h"
 
 using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
 using ::aidl::android::media::tv::tuner::BnTunerFilterCallback;
 using ::aidl::android::media::tv::tuner::ITunerFilter;
 using ::aidl::android::media::tv::tuner::TunerDemuxIpAddress;
@@ -69,10 +71,13 @@
 
 using namespace std;
 
-using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
-
 namespace android {
 
+using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using MQDesc = MQDescriptorSync<uint8_t>;
+using AidlMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
+
 struct SharedHandleInfo {
     native_handle_t* sharedHandle;
     uint64_t size;
@@ -139,7 +144,7 @@
      *
      * @return the actual reading size. -1 if failed to read.
      */
-    int read(uint8_t* buffer, int size);
+    int read(int8_t* buffer, int size);
 
     /**
      * Get the a/v shared memory handle information
@@ -234,7 +239,7 @@
     void getAidlIpAddress(DemuxIpAddress ipAddr,
             TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress);
     Result getFilterMq();
-    int copyData(uint8_t* buffer, int size);
+    int copyData(int8_t* buffer, int size);
     void checkIsMediaFilter(DemuxFilterType type);
     void handleAvShareMemory();
     void closeAvSharedMemory();
@@ -259,7 +264,7 @@
      */
     sp<::android::hardware::tv::tuner::V1_1::IFilter> mFilter_1_1;
 
-    unique_ptr<MQ> mFilterMQ;
+    AidlMQ* mFilterMQ;
     EventFlag* mFilterMQEventFlag;
 
     sp<FilterClientCallback> mCallback;
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 0540aac..f454907 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -21,28 +21,49 @@
 
 #include "FrontendClient.h"
 
-using ::aidl::android::media::tv::tuner::TunerFrontendDvbtSettings;
 using ::aidl::android::media::tv::tuner::TunerFrontendScanAtsc3PlpInfo;
+using ::aidl::android::media::tv::tuner::TunerFrontendUnionSettings;
 
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
 using ::android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Bandwidth;
 using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3TimeInterleaveMode;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbcAnnex;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbcModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSpectralInversion;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbsModulation;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbsStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsRolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendInnerFec;
 using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsRolloff;
 using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Rolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtGuardInterval;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtMode;
 using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo;
-using ::android::hardware::tv::tuner::V1_0::FrontendType;
+using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
 using ::android::hardware::tv::tuner::V1_1::Constant;
+using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval;
 using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth;
 using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode;
 using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
+using ::android::hardware::tv::tuner::V1_1::FrontendType;
 
 namespace android {
 
@@ -86,14 +107,13 @@
 Result FrontendClient::tune(const FrontendSettings& settings,
         const FrontendSettingsExt1_1& settingsExt1_1) {
     if (mTunerFrontend != NULL) {
-        // TODO: aidl frontend settings to include Tuner HAL 1.1 settings
         TunerFrontendSettings tunerFeSettings = getAidlFrontendSettings(settings, settingsExt1_1);
         Status s = mTunerFrontend->tune(tunerFeSettings);
         return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
     Result result;
-    if (mFrontend_1_1 != NULL) {
+    if (mFrontend_1_1 != NULL && validateExtendedSettings(settingsExt1_1)) {
         result = mFrontend_1_1->tune_1_1(settings, settingsExt1_1);
         return result;
     }
@@ -123,14 +143,13 @@
 Result FrontendClient::scan(const FrontendSettings& settings, FrontendScanType type,
         const FrontendSettingsExt1_1& settingsExt1_1) {
     if (mTunerFrontend != NULL) {
-        // TODO: aidl frontend settings to include Tuner HAL 1.1 settings
         TunerFrontendSettings tunerFeSettings = getAidlFrontendSettings(settings, settingsExt1_1);
         Status s = mTunerFrontend->scan(tunerFeSettings, (int)type);
         return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
     Result result;
-    if (mFrontend_1_1 != NULL) {
+    if (mFrontend_1_1 != NULL && validateExtendedSettings(settingsExt1_1)) {
         result = mFrontend_1_1->scan_1_1(settings, type, settingsExt1_1);
         return result;
     }
@@ -161,9 +180,16 @@
     vector<FrontendStatus> status;
 
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*status = mTunerFrontend->getStatus(statusTypes);
-        return status;*/
+        vector<TunerFrontendStatus> aidlStatus;
+        vector<int> types;
+        for (auto t : statusTypes) {
+            types.push_back((int)t);
+        }
+        Status s = mTunerFrontend->getStatus(types, &aidlStatus);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return status;
+        }
+        return getHidlStatus(aidlStatus);
     }
 
     if (mFrontend != NULL && statusTypes.size() > 0) {
@@ -181,14 +207,22 @@
 
     return status;
 }
+
 vector<FrontendStatusExt1_1> FrontendClient::getStatusExtended_1_1(
         vector<FrontendStatusTypeExt1_1> statusTypes) {
     vector<FrontendStatusExt1_1> status;
 
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*status = mTunerFrontend->getStatusExtended_1_1(statusTypes);
-        return status;*/
+        vector<TunerFrontendStatus> aidlStatus;
+        vector<int> types;
+        for (auto t : statusTypes) {
+            types.push_back((int)t);
+        }
+        Status s = mTunerFrontend->getStatusExtended_1_1(types, &aidlStatus);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return status;
+        }
+        return getHidlStatusExt(aidlStatus);
     }
 
     if (mFrontend_1_1 != NULL && statusTypes.size() > 0) {
@@ -293,6 +327,8 @@
     return Result::INVALID_STATE;
 }
 
+/////////////// TunerFrontend Helper Methods ///////////////////////
+
 shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() {
     return mTunerFrontend;
 }
@@ -301,53 +337,454 @@
     return mId;
 }
 
+vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>& aidlStatus) {
+    vector<FrontendStatus> hidlStatus;
+    for (TunerFrontendStatus s : aidlStatus) {
+        FrontendStatus status;
+        switch (s.getTag()) {
+            case TunerFrontendStatus::isDemodLocked: {
+                status.isDemodLocked(s.get<TunerFrontendStatus::isDemodLocked>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::snr: {
+                status.snr(s.get<TunerFrontendStatus::snr>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::ber: {
+                status.ber((uint32_t)s.get<TunerFrontendStatus::ber>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::per: {
+                status.per((uint32_t)s.get<TunerFrontendStatus::per>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::preBer: {
+                status.preBer((uint32_t)s.get<TunerFrontendStatus::preBer>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::signalQuality: {
+                status.signalQuality((uint32_t)s.get<TunerFrontendStatus::signalQuality>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::signalStrength: {
+                status.signalStrength(s.get<TunerFrontendStatus::signalStrength>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::symbolRate: {
+                status.symbolRate((uint32_t)s.get<TunerFrontendStatus::symbolRate>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::innerFec: {
+                status.innerFec(static_cast<FrontendInnerFec>(
+                        s.get<TunerFrontendStatus::innerFec>()));
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::modulation: {
+                auto aidlMod = s.get<TunerFrontendStatus::modulation>();
+                switch (mType) {
+                    case (int)FrontendType::DVBC:
+                        status.modulation().dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DVBS:
+                        status.modulation().dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBS:
+                        status.modulation().isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBS3:
+                        status.modulation().isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBT:
+                        status.modulation().isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
+                        hidlStatus.push_back(status);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            }
+            case TunerFrontendStatus::inversion: {
+                status.inversion(static_cast<FrontendDvbcSpectralInversion>(
+                        s.get<TunerFrontendStatus::inversion>()));
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::lnbVoltage: {
+                status.lnbVoltage(static_cast<LnbVoltage>(
+                        s.get<TunerFrontendStatus::lnbVoltage>()));
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::plpId: {
+                status.plpId((uint8_t)s.get<TunerFrontendStatus::plpId>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isEWBS: {
+                status.isEWBS(s.get<TunerFrontendStatus::isEWBS>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::agc: {
+                status.agc((uint8_t)s.get<TunerFrontendStatus::agc>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isLnaOn: {
+                status.isLnaOn(s.get<TunerFrontendStatus::isLnaOn>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isLayerError: {
+                auto aidlE = s.get<TunerFrontendStatus::isLayerError>();
+                hidl_vec<bool> e(aidlE.begin(), aidlE.end());
+                status.isLayerError(e);
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::mer: {
+                status.mer(s.get<TunerFrontendStatus::mer>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::freqOffset: {
+                status.freqOffset(s.get<TunerFrontendStatus::freqOffset>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::hierarchy: {
+                status.hierarchy(static_cast<FrontendDvbtHierarchy>(
+                        s.get<TunerFrontendStatus::freqOffset>()));
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isRfLocked: {
+                status.isRfLocked(s.get<TunerFrontendStatus::isRfLocked>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::plpInfo: {
+                int size = s.get<TunerFrontendStatus::plpInfo>().size();
+                status.plpInfo().resize(size);
+                for (int i = 0; i < size; i++) {
+                    auto aidlInfo = s.get<TunerFrontendStatus::plpInfo>()[i];
+                    status.plpInfo()[i] = {
+                        .plpId = (uint8_t)aidlInfo.plpId,
+                        .isLocked = aidlInfo.isLocked,
+                        .uec = (uint32_t)aidlInfo.uec,
+                    };
+                }
+                hidlStatus.push_back(status);
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    return hidlStatus;
+}
+
+vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
+        vector<TunerFrontendStatus>& aidlStatus) {
+    vector<FrontendStatusExt1_1> hidlStatus;
+    for (TunerFrontendStatus s : aidlStatus) {
+        FrontendStatusExt1_1 status;
+        switch (s.getTag()) {
+            case TunerFrontendStatus::modulations: {
+                for (auto aidlMod : s.get<TunerFrontendStatus::modulations>()) {
+                    int size = status.modulations().size();
+                    status.modulations().resize(size + 1);
+                    switch (mType) {
+                        case (int)FrontendType::DVBC:
+                            status.modulations()[size].dvbc(
+                                    static_cast<FrontendDvbcModulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::DVBS:
+                            status.modulations()[size].dvbs(
+                                    static_cast<FrontendDvbsModulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::DVBT:
+                            status.modulations()[size].dvbt(
+                                    static_cast<FrontendDvbtConstellation>(aidlMod));
+                            break;
+                        case (int)FrontendType::ISDBS:
+                            status.modulations()[size].isdbs(
+                                    static_cast<FrontendIsdbsModulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::ISDBS3:
+                            status.modulations()[size].isdbs3(
+                                    static_cast<FrontendIsdbs3Modulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::ISDBT:
+                            status.modulations()[size].isdbt(
+                                    static_cast<FrontendIsdbtModulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::ATSC:
+                            status.modulations()[size].atsc(
+                                    static_cast<FrontendAtscModulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::ATSC3:
+                            status.modulations()[size].atsc3(
+                                    static_cast<FrontendAtsc3Modulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::DTMB:
+                            status.modulations()[size].dtmb(
+                                    static_cast<FrontendDtmbModulation>(aidlMod));
+                            break;
+                        default:
+                            status.modulations().resize(size);
+                            break;
+                    }
+                }
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::bers: {
+                auto aidlB = s.get<TunerFrontendStatus::bers>();
+                hidl_vec<uint32_t> b(aidlB.begin(), aidlB.end());
+                status.bers(b);
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::codeRates: {
+                int size = s.get<TunerFrontendStatus::codeRates>().size();
+                status.codeRates().resize(size);
+                for (int i = 0; i < size; i++) {
+                    auto aidlCodeRate = s.get<TunerFrontendStatus::codeRates>()[i];
+                    status.codeRates()[i] =
+                            static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate);
+                }
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::bandwidth: {
+                auto aidlBand = s.get<TunerFrontendStatus::bandwidth>();
+                switch (mType) {
+                    case (int)FrontendType::ATSC3:
+                        status.bandwidth().atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DVBC:
+                        status.bandwidth().dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DVBT:
+                        status.bandwidth().dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBT:
+                        status.bandwidth().isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DTMB:
+                        status.bandwidth().dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand));
+                        hidlStatus.push_back(status);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            }
+            case TunerFrontendStatus::interval: {
+                auto aidlInter = s.get<TunerFrontendStatus::interval>();
+                switch (mType) {
+                    case (int)FrontendType::DVBT:
+                        status.interval().dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBT:
+                        status.interval().isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DTMB:
+                        status.interval().dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter));
+                        hidlStatus.push_back(status);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            }
+            case TunerFrontendStatus::transmissionMode: {
+                auto aidlTran = s.get<TunerFrontendStatus::transmissionMode>();
+                switch (mType) {
+                    case (int)FrontendType::DVBT:
+                        status.transmissionMode().dvbt(
+                                static_cast<FrontendDvbtTransmissionMode>(aidlTran));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBT:
+                        status.transmissionMode().isdbt(static_cast<FrontendIsdbtMode>(aidlTran));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DTMB:
+                        status.transmissionMode().dtmb(
+                                static_cast<FrontendDtmbTransmissionMode>(aidlTran));
+                        hidlStatus.push_back(status);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            }
+            case TunerFrontendStatus::uec: {
+                status.uec((uint32_t)s.get<TunerFrontendStatus::uec>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::systemId: {
+                status.systemId((uint16_t)s.get<TunerFrontendStatus::systemId>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::interleaving: {
+                for (auto aidlInter : s.get<TunerFrontendStatus::interleaving>()) {
+                    int size = status.interleaving().size();
+                    status.interleaving().resize(size + 1);
+                    switch (mType) {
+                        case (int)FrontendType::DVBC:
+                            status.interleaving()[size].dvbc(
+                                    static_cast<FrontendCableTimeInterleaveMode>(aidlInter));
+                            break;
+                        case (int)FrontendType::ATSC3:
+                            status.interleaving()[size].atsc3(
+                                    static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter));
+                            break;
+                        case (int)FrontendType::DTMB:
+                            status.interleaving()[size].dtmb(
+                                    static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter));
+                            break;
+                        default:
+                            status.interleaving().resize(size);
+                            break;
+                    }
+                }
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isdbtSegment: {
+                auto aidlSeg = s.get<TunerFrontendStatus::isdbtSegment>();
+                hidl_vec<uint8_t> s(aidlSeg.begin(), aidlSeg.end());
+                status.isdbtSegment(s);
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::tsDataRate: {
+                auto aidlTs = s.get<TunerFrontendStatus::tsDataRate>();
+                hidl_vec<uint32_t> ts(aidlTs.begin(), aidlTs.end());
+                status.tsDataRate(ts);
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::rollOff: {
+                auto aidlRoll = s.get<TunerFrontendStatus::rollOff>();
+                switch (mType) {
+                    case (int)FrontendType::DVBS:
+                        status.rollOff().dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBS:
+                        status.rollOff().isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBS3:
+                        status.rollOff().isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll));
+                        hidlStatus.push_back(status);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            }
+            case TunerFrontendStatus::isMiso: {
+                status.isMiso(s.get<TunerFrontendStatus::isMiso>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isLinear: {
+                status.isLinear(s.get<TunerFrontendStatus::isLinear>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isShortFrames: {
+                status.isShortFrames(s.get<TunerFrontendStatus::isShortFrames>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    return hidlStatus;
+}
+
 TunerFrontendSettings FrontendClient::getAidlFrontendSettings(const FrontendSettings& settings,
-        const FrontendSettingsExt1_1& /*settingsExt1_1*/) {
-    // TODO: complete hidl to aidl frontend settings conversion
-    TunerFrontendSettings s;
+        const FrontendSettingsExt1_1& settingsExt1_1) {
+    bool isExtended = validateExtendedSettings(settingsExt1_1);
+    TunerFrontendSettings s{
+        .isExtended = isExtended,
+        .endFrequency = (int) settingsExt1_1.endFrequency,
+        .inversion = (int) settingsExt1_1.inversion,
+    };
+
+    if (settingsExt1_1.settingExt.getDiscriminator()
+            == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dtmb) {
+        s.settings.set<TunerFrontendUnionSettings::dtmb>(getAidlDtmbSettings(settingsExt1_1));
+        return s;
+    }
+
     switch (settings.getDiscriminator()) {
         case FrontendSettings::hidl_discriminator::analog: {
+            s.settings.set<TunerFrontendUnionSettings::analog>(
+                    getAidlAnalogSettings(settings, settingsExt1_1));
             break;
         }
         case FrontendSettings::hidl_discriminator::atsc: {
+            s.settings.set<TunerFrontendUnionSettings::atsc>(getAidlAtscSettings(settings));
             break;
         }
         case FrontendSettings::hidl_discriminator::atsc3: {
+            s.settings.set<TunerFrontendUnionSettings::atsc3>(getAidlAtsc3Settings(settings));
             break;
         }
         case FrontendSettings::hidl_discriminator::dvbs: {
+            s.settings.set<TunerFrontendUnionSettings::dvbs>(
+                    getAidlDvbsSettings(settings, settingsExt1_1));
             break;
         }
         case FrontendSettings::hidl_discriminator::dvbc: {
+            s.settings.set<TunerFrontendUnionSettings::cable>(
+                    getAidlCableSettings(settings, settingsExt1_1));
             break;
         }
         case FrontendSettings::hidl_discriminator::dvbt: {
-            TunerFrontendDvbtSettings dvbtSettings{
-                .frequency = (int)settings.dvbt().frequency,
-                .transmissionMode = (int)settings.dvbt().transmissionMode,
-                .bandwidth = (int)settings.dvbt().bandwidth,
-                .constellation = (int)settings.dvbt().constellation,
-                .hierarchy = (int)settings.dvbt().hierarchy,
-                .hpCodeRate = (int)settings.dvbt().hpCoderate,
-                .lpCodeRate = (int)settings.dvbt().lpCoderate,
-                .guardInterval = (int)settings.dvbt().guardInterval,
-                .isHighPriority = settings.dvbt().isHighPriority,
-                .standard = (int)settings.dvbt().standard,
-                .isMiso = settings.dvbt().isMiso,
-                .plpMode = (int)settings.dvbt().plpMode,
-                .plpId = (int)settings.dvbt().plpId,
-                .plpGroupId = (int)settings.dvbt().plpGroupId,
-            };
-            s.set<TunerFrontendSettings::dvbt>(dvbtSettings);
+            s.settings.set<TunerFrontendUnionSettings::dvbt>(
+                    getAidlDvbtSettings(settings, settingsExt1_1));
             break;
         }
         case FrontendSettings::hidl_discriminator::isdbs: {
+            s.settings.set<TunerFrontendUnionSettings::isdbs>(getAidlIsdbsSettings(settings));
             break;
         }
         case FrontendSettings::hidl_discriminator::isdbs3: {
+            s.settings.set<TunerFrontendUnionSettings::isdbs3>(getAidlIsdbs3Settings(settings));
             break;
         }
         case FrontendSettings::hidl_discriminator::isdbt: {
+            s.settings.set<TunerFrontendUnionSettings::isdbt>(getAidlIsdbtSettings(settings));
             break;
         }
         default:
@@ -356,6 +793,192 @@
     return s;
 }
 
+TunerFrontendAnalogSettings FrontendClient::getAidlAnalogSettings(const FrontendSettings& settings,
+        const FrontendSettingsExt1_1& settingsExt1_1) {
+    TunerFrontendAnalogSettings analogSettings{
+        .frequency = (int)settings.analog().frequency,
+        .signalType = (int)settings.analog().type,
+        .sifStandard = (int)settings.analog().sifStandard,
+    };
+    if (settingsExt1_1.settingExt.getDiscriminator()
+            == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::analog) {
+        analogSettings.isExtended = true;
+        analogSettings.aftFlag = (int)settingsExt1_1.settingExt.analog().aftFlag;
+    } else {
+        analogSettings.isExtended = false;
+    }
+    return analogSettings;
+}
+
+TunerFrontendDvbsSettings FrontendClient::getAidlDvbsSettings(const FrontendSettings& settings,
+        const FrontendSettingsExt1_1& settingsExt1_1) {
+    TunerFrontendDvbsSettings dvbsSettings{
+        .frequency = (int)settings.dvbs().frequency,
+        .modulation = (int)settings.dvbs().modulation,
+        .codeRate = {
+            .fec = (long)settings.dvbs().coderate.fec,
+            .isLinear = settings.dvbs().coderate.isLinear,
+            .isShortFrames = settings.dvbs().coderate.isShortFrames,
+            .bitsPer1000Symbol = (int)settings.dvbs().coderate.bitsPer1000Symbol,
+        },
+        .symbolRate = (int)settings.dvbs().symbolRate,
+        .rolloff = (int)settings.dvbs().rolloff,
+        .pilot = (int)settings.dvbs().pilot,
+        .inputStreamId = (int)settings.dvbs().inputStreamId,
+        .standard = (int)settings.dvbs().standard,
+        .vcm = (int)settings.dvbs().vcmMode,
+    };
+    if (settingsExt1_1.settingExt.getDiscriminator()
+            == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbs) {
+        dvbsSettings.isExtended = true;
+        dvbsSettings.scanType = (int)settingsExt1_1.settingExt.dvbs().scanType;
+        dvbsSettings.isDiseqcRxMessage = settingsExt1_1.settingExt.dvbs().isDiseqcRxMessage;
+    } else {
+        dvbsSettings.isExtended = false;
+    }
+    return dvbsSettings;
+}
+
+TunerFrontendCableSettings FrontendClient::getAidlCableSettings(const FrontendSettings& settings,
+        const FrontendSettingsExt1_1& settingsExt1_1) {
+    TunerFrontendCableSettings cableSettings{
+        .frequency = (int)settings.dvbc().frequency,
+        .modulation = (int)settings.dvbc().modulation,
+        .innerFec = (long)settings.dvbc().fec,
+        .symbolRate = (int)settings.dvbc().symbolRate,
+        .outerFec = (int)settings.dvbc().outerFec,
+        .annex = (int)settings.dvbc().annex,
+        .spectralInversion = (int)settings.dvbc().spectralInversion,
+    };
+    if (settingsExt1_1.settingExt.getDiscriminator()
+            == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbc) {
+        cableSettings.isExtended = true;
+        cableSettings.interleaveMode = (int)settingsExt1_1.settingExt.dvbc().interleaveMode;
+        cableSettings.bandwidth = (int)settingsExt1_1.settingExt.dvbc().bandwidth;
+    } else {
+        cableSettings.isExtended = false;
+    }
+    return cableSettings;
+}
+
+TunerFrontendDvbtSettings FrontendClient::getAidlDvbtSettings(const FrontendSettings& settings,
+        const FrontendSettingsExt1_1& settingsExt1_1) {
+    TunerFrontendDvbtSettings dvbtSettings{
+        .frequency = (int)settings.dvbt().frequency,
+        .transmissionMode = (int)settings.dvbt().transmissionMode,
+        .bandwidth = (int)settings.dvbt().bandwidth,
+        .constellation = (int)settings.dvbt().constellation,
+        .hierarchy = (int)settings.dvbt().hierarchy,
+        .hpCodeRate = (int)settings.dvbt().hpCoderate,
+        .lpCodeRate = (int)settings.dvbt().lpCoderate,
+        .guardInterval = (int)settings.dvbt().guardInterval,
+        .isHighPriority = settings.dvbt().isHighPriority,
+        .standard = (int)settings.dvbt().standard,
+        .isMiso = settings.dvbt().isMiso,
+        .plpMode = (int)settings.dvbt().plpMode,
+        .plpId = (int)settings.dvbt().plpId,
+        .plpGroupId = (int)settings.dvbt().plpGroupId,
+    };
+    if (settingsExt1_1.settingExt.getDiscriminator()
+            == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbt) {
+        dvbtSettings.isExtended = true;
+        dvbtSettings.constellation = (int)settingsExt1_1.settingExt.dvbt().constellation;
+        dvbtSettings.transmissionMode =
+                (int)settingsExt1_1.settingExt.dvbt().transmissionMode;
+    } else {
+        dvbtSettings.isExtended = false;
+    }
+    return dvbtSettings;
+}
+
+TunerFrontendDtmbSettings FrontendClient::getAidlDtmbSettings(
+        const FrontendSettingsExt1_1& settingsExt1_1) {
+    TunerFrontendDtmbSettings dtmbSettings{
+        .frequency = (int)settingsExt1_1.settingExt.dtmb().frequency,
+        .transmissionMode = (int)settingsExt1_1.settingExt.dtmb().transmissionMode,
+        .bandwidth = (int)settingsExt1_1.settingExt.dtmb().bandwidth,
+        .modulation = (int)settingsExt1_1.settingExt.dtmb().modulation,
+        .codeRate = (int)settingsExt1_1.settingExt.dtmb().codeRate,
+        .guardInterval = (int)settingsExt1_1.settingExt.dtmb().guardInterval,
+        .interleaveMode = (int)settingsExt1_1.settingExt.dtmb().interleaveMode,
+    };
+    return dtmbSettings;
+}
+
+TunerFrontendAtscSettings FrontendClient::getAidlAtscSettings(const FrontendSettings& settings) {
+    TunerFrontendAtscSettings atscSettings{
+        .frequency = (int)settings.atsc().frequency,
+        .modulation = (int)settings.atsc().modulation,
+    };
+    return atscSettings;
+}
+
+TunerFrontendAtsc3Settings FrontendClient::getAidlAtsc3Settings(const FrontendSettings& settings) {
+    TunerFrontendAtsc3Settings atsc3Settings{
+        .frequency = (int)settings.atsc3().frequency,
+        .bandwidth = (int)settings.atsc3().bandwidth,
+        .demodOutputFormat = (int)settings.atsc3().demodOutputFormat,
+    };
+    atsc3Settings.plpSettings.resize(settings.atsc3().plpSettings.size());
+    for (auto plpSetting : settings.atsc3().plpSettings) {
+        atsc3Settings.plpSettings.push_back({
+            .plpId = (int)plpSetting.plpId,
+            .modulation = (int)plpSetting.modulation,
+            .interleaveMode = (int)plpSetting.interleaveMode,
+            .codeRate = (int)plpSetting.codeRate,
+            .fec = (int)plpSetting.fec,
+        });
+    }
+    return atsc3Settings;
+}
+
+TunerFrontendIsdbsSettings FrontendClient::getAidlIsdbsSettings(const FrontendSettings& settings) {
+    TunerFrontendIsdbsSettings isdbsSettings{
+        .frequency = (int)settings.isdbs().frequency,
+        .streamId = (int)settings.isdbs().streamId,
+        .streamIdType = (int)settings.isdbs().streamIdType,
+        .modulation = (int)settings.isdbs().modulation,
+        .codeRate = (int)settings.isdbs().coderate,
+        .symbolRate = (int)settings.isdbs().symbolRate,
+        .rolloff = (int)settings.isdbs().rolloff,
+    };
+    return isdbsSettings;
+}
+
+TunerFrontendIsdbs3Settings FrontendClient::getAidlIsdbs3Settings(
+        const FrontendSettings& settings) {
+    TunerFrontendIsdbs3Settings isdbs3Settings{
+        .frequency = (int)settings.isdbs3().frequency,
+        .streamId = (int)settings.isdbs3().streamId,
+        .streamIdType = (int)settings.isdbs3().streamIdType,
+        .modulation = (int)settings.isdbs3().modulation,
+        .codeRate = (int)settings.isdbs3().coderate,
+        .symbolRate = (int)settings.isdbs3().symbolRate,
+        .rolloff = (int)settings.isdbs3().rolloff,
+    };
+    return isdbs3Settings;
+}
+
+TunerFrontendIsdbtSettings FrontendClient::getAidlIsdbtSettings(const FrontendSettings& settings) {
+    TunerFrontendIsdbtSettings isdbtSettings{
+        .frequency = (int)settings.isdbt().frequency,
+        .modulation = (int)settings.isdbt().modulation,
+        .bandwidth = (int)settings.isdbt().bandwidth,
+        .mode = (int)settings.isdbt().mode,
+        .codeRate = (int)settings.isdbt().coderate,
+        .guardInterval = (int)settings.isdbt().guardInterval,
+        .serviceAreaId = (int)settings.isdbt().serviceAreaId,
+    };
+    return isdbtSettings;
+}
+
+bool FrontendClient::validateExtendedSettings(const FrontendSettingsExt1_1& settingsExt1_1) {
+    return settingsExt1_1.endFrequency != (uint32_t)Constant::INVALID_FRONTEND_SETTING_FREQUENCY
+            || settingsExt1_1.inversion != FrontendSpectralInversion::UNDEFINED
+            || settingsExt1_1.settingExt.getDiscriminator()
+                    != FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::noinit;
+}
+
 /////////////// TunerFrontendCallback ///////////////////////
 
 TunerFrontendCallback::TunerFrontendCallback(sp<FrontendClientCallback> frontendClientCallback)
@@ -492,14 +1115,15 @@
             vector<TunerFrontendScanAtsc3PlpInfo> plp =
                     message.get<TunerFrontendScanMessage::atsc3PlpInfos>();
             hidl_vec<FrontendScanAtsc3PlpInfo> plpInfo;
-            for (TunerFrontendScanAtsc3PlpInfo info : plp) {
+            int size = plp.size();
+            plpInfo.resize(size);
+            for (int i = 0; i < size; i++) {
+                auto info = message.get<TunerFrontendScanMessage::atsc3PlpInfos>()[i];
                 FrontendScanAtsc3PlpInfo p{
                     .plpId = static_cast<uint8_t>(info.plpId),
                     .bLlsFlag = info.llsFlag,
                 };
-                int size = plpInfo.size();
-                plpInfo.resize(size + 1);
-                plpInfo[size] = p;
+                plpInfo[i] = p;
             }
             scanMessage.atsc3PlpInfos(plpInfo);
             break;
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index 17fd583..298b397 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -31,8 +31,19 @@
 
 using ::aidl::android::media::tv::tuner::BnTunerFrontendCallback;
 using ::aidl::android::media::tv::tuner::ITunerFrontend;
+using ::aidl::android::media::tv::tuner::TunerFrontendAnalogSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendAtscSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendAtsc3Settings;
+using ::aidl::android::media::tv::tuner::TunerFrontendCableSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendDvbsSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendDvbtSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendDtmbSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbsSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbs3Settings;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbtSettings;
 using ::aidl::android::media::tv::tuner::TunerFrontendScanMessage;
 using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendStatus;
 
 using ::android::hardware::Return;
 using ::android::hardware::Void;
@@ -172,8 +183,27 @@
     int getId();
 
 private:
-    TunerFrontendSettings getAidlFrontendSettings(const FrontendSettings& settings,
-            const FrontendSettingsExt1_1& settingsExt1_1);
+    vector<FrontendStatus> getHidlStatus(vector<TunerFrontendStatus>& aidlStatus);
+    vector<FrontendStatusExt1_1> getHidlStatusExt(vector<TunerFrontendStatus>& aidlStatus);
+
+    TunerFrontendSettings getAidlFrontendSettings(
+            const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+    TunerFrontendAnalogSettings getAidlAnalogSettings(
+            const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+    TunerFrontendDvbsSettings getAidlDvbsSettings(
+            const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+    TunerFrontendCableSettings getAidlCableSettings(
+            const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+    TunerFrontendDvbtSettings getAidlDvbtSettings(
+            const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+    TunerFrontendDtmbSettings getAidlDtmbSettings(const FrontendSettingsExt1_1& settingsExt1_1);
+    TunerFrontendAtscSettings getAidlAtscSettings(const FrontendSettings& settings);
+    TunerFrontendAtsc3Settings getAidlAtsc3Settings(const FrontendSettings& settings);
+    TunerFrontendIsdbsSettings getAidlIsdbsSettings(const FrontendSettings& settings);
+    TunerFrontendIsdbs3Settings getAidlIsdbs3Settings(const FrontendSettings& settings);
+    TunerFrontendIsdbtSettings getAidlIsdbtSettings(const FrontendSettings& settings);
+
+    bool validateExtendedSettings(const FrontendSettingsExt1_1& settingsExt1_1);
 
     /**
      * An AIDL Tuner Frontend Singleton assigned at the first time when the Tuner Client
diff --git a/media/jni/tuner/TimeFilterClient.cpp b/media/jni/tuner/TimeFilterClient.cpp
index 27ea6e5..432238d 100644
--- a/media/jni/tuner/TimeFilterClient.cpp
+++ b/media/jni/tuner/TimeFilterClient.cpp
@@ -19,6 +19,7 @@
 #include <android-base/logging.h>
 #include <utils/Log.h>
 
+#include "ClientHelper.h"
 #include "TimeFilterClient.h"
 
 using ::android::hardware::tv::tuner::V1_0::Result;
@@ -28,13 +29,12 @@
 
 /////////////// TimeFilterClient ///////////////////////
 
-// TODO: pending aidl interface
-TimeFilterClient::TimeFilterClient() {
-    //mTunerTimeFilter = tunerTimeFilter;
+TimeFilterClient::TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter) {
+    mTunerTimeFilter = tunerTimeFilter;
 }
 
 TimeFilterClient::~TimeFilterClient() {
-    //mTunerTimeFilter = NULL;
+    mTunerTimeFilter = NULL;
     mTimeFilter = NULL;
 }
 
@@ -44,7 +44,10 @@
 }
 
 Result TimeFilterClient::setTimeStamp(long timeStamp) {
-    // TODO: pending aidl interface
+    if (mTunerTimeFilter != NULL) {
+        Status s = mTunerTimeFilter->setTimeStamp(timeStamp);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mTimeFilter != NULL) {
         return mTimeFilter->setTimeStamp(timeStamp);
@@ -54,7 +57,10 @@
 }
 
 Result TimeFilterClient::clearTimeStamp() {
-    // TODO: pending aidl interface
+    if (mTunerTimeFilter != NULL) {
+        Status s = mTunerTimeFilter->clearTimeStamp();
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mTimeFilter != NULL) {
         return mTimeFilter->clearTimeStamp();
@@ -64,7 +70,14 @@
 }
 
 long TimeFilterClient::getTimeStamp() {
-    // TODO: pending aidl interface
+    if (mTunerTimeFilter != NULL) {
+        int64_t timeStamp;
+        Status s = mTunerTimeFilter->getTimeStamp(&timeStamp);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+        }
+        return timeStamp;
+    }
 
     if (mTimeFilter != NULL) {
         Result res;
@@ -84,27 +97,37 @@
 }
 
 long TimeFilterClient::getSourceTime() {
-    // TODO: pending aidl interface
+    if (mTunerTimeFilter != NULL) {
+        int64_t sourceTime;
+        Status s = mTunerTimeFilter->getTimeStamp(&sourceTime);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+        }
+        return sourceTime;
+    }
 
     if (mTimeFilter != NULL) {
         Result res;
-        long timestamp;
+        long sourceTime;
         mTimeFilter->getSourceTime(
                 [&](Result r, uint64_t t) {
                     res = r;
-                    timestamp = t;
+                    sourceTime = t;
                 });
         if (res != Result::SUCCESS) {
             return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
         }
-        return timestamp;
+        return sourceTime;
     }
 
     return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
 }
 
 Result TimeFilterClient::close() {
-    // TODO: pending aidl interface
+    if (mTunerTimeFilter != NULL) {
+        Status s = mTunerTimeFilter->close();
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mTimeFilter != NULL) {
         return mTimeFilter->close();
diff --git a/media/jni/tuner/TimeFilterClient.h b/media/jni/tuner/TimeFilterClient.h
index 9a9d172..56ddd68 100644
--- a/media/jni/tuner/TimeFilterClient.h
+++ b/media/jni/tuner/TimeFilterClient.h
@@ -17,12 +17,13 @@
 #ifndef _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
 #define _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
 
-//#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
+#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
 #include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
 #include <android/hardware/tv/tuner/1.1/types.h>
 
-//using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
 
+using Status = ::ndk::ScopedAStatus;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 using ::android::hardware::hidl_vec;
@@ -36,8 +37,7 @@
 struct TimeFilterClient : public RefBase {
 
 public:
-    // TODO: add TunerTimeFilter as parameter.
-    TimeFilterClient();
+    TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter);
     ~TimeFilterClient();
 
     // TODO: remove after migration to Tuner Service is done.
@@ -73,8 +73,7 @@
      * An AIDL Tuner TimeFilter Singleton assigned at the first time the Tuner Client
      * opens an TimeFilter. Default null when time filter is not opened.
      */
-    // TODO: pending on aidl interface
-    //shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
+    shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
 
     /**
      * A TimeFilter HAL interface that is ready before migrating to the TunerTimeFilter.
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index 14393a1..a604490d 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -200,7 +200,14 @@
 }
 
 shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() {
-    // pending aidl interface
+    if (mTunerService != NULL) {
+        TunerDemuxCapabilities aidlCaps;
+        Status s = mTunerService->getDemuxCaps(&aidlCaps);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return NULL;
+        }
+        return make_shared<DemuxCapabilities>(getHidlDemuxCaps(aidlCaps));
+    }
 
     if (mTuner != NULL) {
         Result res;
@@ -459,6 +466,26 @@
     return descrambler;
 }
 
+DemuxCapabilities TunerClient::getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps) {
+    DemuxCapabilities caps{
+        .numDemux = (uint32_t)aidlCaps.numDemux,
+        .numRecord = (uint32_t)aidlCaps.numRecord,
+        .numPlayback = (uint32_t)aidlCaps.numPlayback,
+        .numTsFilter = (uint32_t)aidlCaps.numTsFilter,
+        .numSectionFilter = (uint32_t)aidlCaps.numSectionFilter,
+        .numAudioFilter = (uint32_t)aidlCaps.numAudioFilter,
+        .numVideoFilter = (uint32_t)aidlCaps.numVideoFilter,
+        .numPesFilter = (uint32_t)aidlCaps.numPesFilter,
+        .numPcrFilter = (uint32_t)aidlCaps.numPcrFilter,
+        .numBytesInSectionFilter = (uint32_t)aidlCaps.numBytesInSectionFilter,
+        .filterCaps = (uint32_t)aidlCaps.filterCaps,
+        .bTimeFilter = aidlCaps.bTimeFilter,
+    };
+    caps.linkCaps.resize(aidlCaps.linkCaps.size());
+    copy(aidlCaps.linkCaps.begin(), aidlCaps.linkCaps.end(), caps.linkCaps.begin());
+    return caps;
+}
+
 FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
     FrontendInfo hidlFrontendInfo {
         .type = static_cast<FrontendType>(aidlFrontendInfo.type),
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 6ce6661..acd018e 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -32,6 +32,7 @@
 
 using Status = ::ndk::ScopedAStatus;
 
+using ::aidl::android::media::tv::tuner::TunerDemuxCapabilities;
 using ::aidl::android::media::tv::tuner::ITunerService;
 using ::aidl::android::media::tv::tuner::TunerFrontendInfo;
 using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager;
@@ -145,6 +146,7 @@
     sp<ILnb> openHidlLnbByName(string name, LnbId& lnbId);
     sp<IDescrambler> openHidlDescrambler();
     vector<int> getLnbHandles();
+    DemuxCapabilities getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps);
     FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
     void updateTunerResources();
     void updateFrontendResources();
diff --git a/native/android/OWNERS b/native/android/OWNERS
index ac5a895..d414ed4 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -1,3 +1,4 @@
 per-file libandroid_net.map.txt, net.c = set noparent
 per-file libandroid_net.map.txt, net.c = codewiz@google.com, jchalard@google.com, junyulai@google.com
 per-file libandroid_net.map.txt, net.c = lorenzo@google.com, reminv@google.com, satk@google.com
+per-file system_fonts.cpp = file:/graphics/java/android/graphics/fonts/OWNERS
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 620c7ae..72589e3 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -18,6 +18,7 @@
 
 import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
 import static android.text.TextUtils.emptyIfNull;
+import static android.text.TextUtils.isEmpty;
 import static android.text.TextUtils.withoutPrefix;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
@@ -67,7 +68,13 @@
         getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
 
         String deviceProfile = getRequest().getDeviceProfile();
-        String profileName = getDeviceProfileName(deviceProfile);
+        String profilePrivacyDisclaimer = emptyIfNull(getRequest()
+                .getDeviceProfilePrivilegesDescription())
+                .replace("APP_NAME", getCallingAppName());
+        boolean useDeviceProfile = deviceProfile != null && !isEmpty(profilePrivacyDisclaimer);
+        String profileName = useDeviceProfile
+                ? getDeviceProfileName(deviceProfile)
+                : getString(R.string.profile_name_generic);
 
         if (getRequest().isSingleDevice()) {
             setContentView(R.layout.device_confirmation);
@@ -110,15 +117,12 @@
 
         TextView profileSummary = findViewById(R.id.profile_summary);
 
-        if (deviceProfile != null) {
-            String privacyDisclaimer = emptyIfNull(getRequest()
-                    .getDeviceProfilePrivilegesDescription())
-                    .replace("APP_NAME", getCallingAppName());
+        if (useDeviceProfile) {
             profileSummary.setVisibility(View.VISIBLE);
             profileSummary.setText(getString(R.string.profile_summary,
                     getCallingAppName(),
                     profileName,
-                    privacyDisclaimer));
+                    profilePrivacyDisclaimer));
         } else {
             profileSummary.setVisibility(View.GONE);
         }
@@ -142,7 +146,7 @@
                 return getString(R.string.profile_name_watch);
             }
             default: {
-                Log.wtf(LOG_TAG,
+                Log.w(LOG_TAG,
                         "No localized profile name found for device profile: " + deviceProfile);
                 return withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile)
                         .toLowerCase()
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 2a0a74e..fa3213d 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -3231,32 +3231,6 @@
         }
     }
 
-    /** {@hide} - returns the factory serial number */
-    @UnsupportedAppUsage
-    @RequiresPermission(anyOf = {
-            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_FACTORY})
-    public int registerNetworkFactory(Messenger messenger, String name) {
-        try {
-            return mService.registerNetworkFactory(messenger, name);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /** {@hide} */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    @RequiresPermission(anyOf = {
-            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_FACTORY})
-    public void unregisterNetworkFactory(Messenger messenger) {
-        try {
-            mService.unregisterNetworkFactory(messenger);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     /**
      * Registers the specified {@link NetworkProvider}.
      * Each listener must only be registered once. The listener can be unregistered with
@@ -4609,7 +4583,7 @@
             // Set HTTP proxy system properties to match network.
             // TODO: Deprecate this static method and replace it with a non-static version.
             try {
-                Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy());
+                Proxy.setHttpProxyConfiguration(getInstance().getDefaultProxy());
             } catch (SecurityException e) {
                 // The process doesn't have ACCESS_NETWORK_STATE, so we can't fetch the proxy.
                 Log.e(TAG, "Can't set proxy properties", e);
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 1b4d2e4..db8b7ed 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -156,9 +156,6 @@
 
     boolean requestBandwidthUpdate(in Network network);
 
-    int registerNetworkFactory(in Messenger messenger, in String name);
-    void unregisterNetworkFactory(in Messenger messenger);
-
     int registerNetworkProvider(in Messenger messenger, in String name);
     void unregisterNetworkProvider(in Messenger messenger);
 
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index d22d82d..27aa15d 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -20,6 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -775,7 +776,8 @@
      * @param underlyingNetworks the new list of underlying networks.
      * @see {@link VpnService.Builder#setUnderlyingNetworks(Network[])}
      */
-    public final void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) {
+    public final void setUnderlyingNetworks(
+            @SuppressLint("NullableCollection") @Nullable List<Network> underlyingNetworks) {
         final ArrayList<Network> underlyingArray = (underlyingNetworks != null)
                 ? new ArrayList<>(underlyingNetworks) : null;
         queueOrSendMessage(reg -> reg.sendUnderlyingNetworks(underlyingArray));
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 8bfa77a..55b2c3c 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -1786,6 +1786,15 @@
         return 0;
     }
 
+    private <T extends Parcelable> void writeParcelableArraySet(Parcel in,
+            @Nullable ArraySet<T> val, int flags) {
+        final int size = (val != null) ? val.size() : -1;
+        in.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            in.writeParcelable(val.valueAt(i), flags);
+        }
+    }
+
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(mNetworkCapabilities);
@@ -1796,7 +1805,7 @@
         dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
         dest.writeParcelable((Parcelable) mTransportInfo, flags);
         dest.writeInt(mSignalStrength);
-        dest.writeArraySet(mUids);
+        writeParcelableArraySet(dest, mUids, flags);
         dest.writeString(mSSID);
         dest.writeBoolean(mPrivateDnsBroken);
         dest.writeIntArray(getAdministratorUids());
@@ -1819,8 +1828,7 @@
                 netCap.mNetworkSpecifier = in.readParcelable(null);
                 netCap.mTransportInfo = in.readParcelable(null);
                 netCap.mSignalStrength = in.readInt();
-                netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
-                        null /* ClassLoader, null for default */);
+                netCap.mUids = readParcelableArraySet(in, null /* ClassLoader, null for default */);
                 netCap.mSSID = in.readString();
                 netCap.mPrivateDnsBroken = in.readBoolean();
                 netCap.setAdministratorUids(in.createIntArray());
@@ -1833,6 +1841,20 @@
             public NetworkCapabilities[] newArray(int size) {
                 return new NetworkCapabilities[size];
             }
+
+            private @Nullable <T extends Parcelable> ArraySet<T> readParcelableArraySet(Parcel in,
+                    @Nullable ClassLoader loader) {
+                final int size = in.readInt();
+                if (size < 0) {
+                    return null;
+                }
+                final ArraySet<T> result = new ArraySet<>(size);
+                for (int i = 0; i < size; i++) {
+                    final T value = in.readParcelable(loader);
+                    result.append(value);
+                }
+                return result;
+            }
         };
 
     @Override
diff --git a/packages/Connectivity/framework/src/android/net/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java
index 03b07e0..03cfbbb 100644
--- a/packages/Connectivity/framework/src/android/net/Proxy.java
+++ b/packages/Connectivity/framework/src/android/net/Proxy.java
@@ -16,8 +16,10 @@
 
 package android.net;
 
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Build;
@@ -245,7 +247,19 @@
 
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final void setHttpProxySystemProperty(ProxyInfo p) {
+    @Deprecated
+    public static void setHttpProxySystemProperty(ProxyInfo p) {
+        setHttpProxyConfiguration(p);
+    }
+
+    /**
+     * Set HTTP proxy configuration for the process to match the provided ProxyInfo.
+     *
+     * If the provided ProxyInfo is null, the proxy configuration will be cleared.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static void setHttpProxyConfiguration(@Nullable ProxyInfo p) {
         String host = null;
         String port = null;
         String exclList = null;
@@ -256,11 +270,11 @@
             exclList = ProxyUtils.exclusionListAsString(p.getExclusionList());
             pacFileUrl = p.getPacFileUrl();
         }
-        setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
+        setHttpProxyConfiguration(host, port, exclList, pacFileUrl);
     }
 
     /** @hide */
-    public static final void setHttpProxySystemProperty(String host, String port, String exclList,
+    public static void setHttpProxyConfiguration(String host, String port, String exclList,
             Uri pacFileUrl) {
         if (exclList != null) exclList = exclList.replace(",", "|");
         if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
index d472311..7cc5994 100644
--- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -26,7 +26,6 @@
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationRequest;
-import android.location.LocationResult;
 import android.location.provider.ILocationProvider;
 import android.location.provider.ILocationProviderManager;
 import android.location.provider.ProviderProperties;
@@ -49,6 +48,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.util.List;
 import java.util.Random;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -167,10 +167,13 @@
         }
 
         @Override
-        public void onReportLocation(LocationResult locationResult) {
-            for (int i = 0; i < locationResult.size(); i++) {
-                mLocations.add(locationResult.get(i));
-            }
+        public void onReportLocation(Location location) {
+            mLocations.add(location);
+        }
+
+        @Override
+        public void onReportLocations(List<Location> locations) {
+            mLocations.addAll(locations);
         }
 
         @Override
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 44864a6..a6e2af9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -42,6 +42,7 @@
 import android.provider.settings.validators.SecureSettingsValidators;
 import android.provider.settings.validators.SystemSettingsValidators;
 import android.provider.settings.validators.Validator;
+import android.telephony.SubscriptionManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.BackupUtils;
@@ -95,10 +96,11 @@
     private static final String KEY_NETWORK_POLICIES = "network_policies";
     private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
     private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
+    private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
 
     // Versioning of the state file.  Increment this version
     // number any time the set of state items is altered.
-    private static final int STATE_VERSION = 8;
+    private static final int STATE_VERSION = 9;
 
     // Versioning of the Network Policies backup payload.
     private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
@@ -106,19 +108,20 @@
 
     // Slots in the checksum array.  Never insert new items in the middle
     // of this array; new slots must be appended.
-    private static final int STATE_SYSTEM           = 0;
-    private static final int STATE_SECURE           = 1;
-    private static final int STATE_LOCALE           = 2;
-    private static final int STATE_WIFI_SUPPLICANT  = 3;
-    private static final int STATE_WIFI_CONFIG      = 4;
-    private static final int STATE_GLOBAL           = 5;
-    private static final int STATE_LOCK_SETTINGS    = 6;
-    private static final int STATE_SOFTAP_CONFIG    = 7;
-    private static final int STATE_NETWORK_POLICIES = 8;
-    private static final int STATE_WIFI_NEW_CONFIG  = 9;
-    private static final int STATE_DEVICE_CONFIG    = 10;
+    private static final int STATE_SYSTEM                = 0;
+    private static final int STATE_SECURE                = 1;
+    private static final int STATE_LOCALE                = 2;
+    private static final int STATE_WIFI_SUPPLICANT       = 3;
+    private static final int STATE_WIFI_CONFIG           = 4;
+    private static final int STATE_GLOBAL                = 5;
+    private static final int STATE_LOCK_SETTINGS         = 6;
+    private static final int STATE_SOFTAP_CONFIG         = 7;
+    private static final int STATE_NETWORK_POLICIES      = 8;
+    private static final int STATE_WIFI_NEW_CONFIG       = 9;
+    private static final int STATE_DEVICE_CONFIG         = 10;
+    private static final int STATE_SIM_SPECIFIC_SETTINGS = 11;
 
-    private static final int STATE_SIZE             = 11; // The current number of state items
+    private static final int STATE_SIZE                  = 12; // The current number of state items
 
     // Number of entries in the checksum array at various version numbers
     private static final int STATE_SIZES[] = {
@@ -130,7 +133,8 @@
             8,              // version 5 added STATE_SOFTAP_CONFIG
             9,              // version 6 added STATE_NETWORK_POLICIES
             10,             // version 7 added STATE_WIFI_NEW_CONFIG
-            STATE_SIZE      // version 8 added STATE_DEVICE_CONFIG
+            11,             // version 8 added STATE_DEVICE_CONFIG
+            STATE_SIZE      // version 9 added STATE_SIM_SPECIFIC_SETTINGS
     };
 
     private static final int FULL_BACKUP_ADDED_GLOBAL = 2;  // added the "global" entry
@@ -218,6 +222,7 @@
         byte[] netPoliciesData = getNetworkPolicies();
         byte[] wifiFullConfigData = getNewWifiConfigData();
         byte[] deviceSpecificInformation = getDeviceSpecificConfiguration();
+        byte[] simSpecificSettingsData = getSimSpecificSettingsData();
 
         long[] stateChecksums = readOldChecksums(oldState);
 
@@ -246,6 +251,9 @@
         stateChecksums[STATE_DEVICE_CONFIG] =
                 writeIfChanged(stateChecksums[STATE_DEVICE_CONFIG], KEY_DEVICE_SPECIFIC_CONFIG,
                         deviceSpecificInformation, data);
+        stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] =
+                writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS],
+                        KEY_SIM_SPECIFIC_SETTINGS, simSpecificSettingsData, data);
 
         writeNewChecksums(stateChecksums, newState);
     }
@@ -386,6 +394,12 @@
                             preservedSettings);
                     break;
 
+                case KEY_SIM_SPECIFIC_SETTINGS:
+                    byte[] restoredSimSpecificSettings = new byte[size];
+                    data.readEntityData(restoredSimSpecificSettings, 0, size);
+                    restoreSimSpecificSettings(restoredSimSpecificSettings);
+                    break;
+
                 default :
                     data.skipEntityData();
 
@@ -1189,6 +1203,20 @@
         return true;
     }
 
+    private byte[] getSimSpecificSettingsData() {
+        SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+        byte[] simSpecificData = subManager.getAllSimSpecificSettingsForBackup();
+        Log.i(TAG, "sim specific data of length + " + simSpecificData.length
+                + " successfully retrieved");
+
+        return simSpecificData;
+    }
+
+    private void restoreSimSpecificSettings(byte[] data) {
+        SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+        subManager.restoreAllSimSpecificSettingsFromBackup(data);
+    }
+
     private void updateWindowManagerIfNeeded(Integer previousDensity) {
         int newDensity;
         try {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1af7781..eab0990 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -122,6 +122,8 @@
     <uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" />
     <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
     <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
+    <uses-permission android:name="android.permission.QUERY_USERS" />
+    <uses-permission android:name="android.permission.MODIFY_QUIET_MODE" />
     <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/>
     <uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE"/>
     <uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL"/>
diff --git a/packages/SystemUI/docs/sos_gesture.md b/packages/SystemUI/docs/sos_gesture.md
new file mode 100644
index 0000000..1a9144b
--- /dev/null
+++ b/packages/SystemUI/docs/sos_gesture.md
@@ -0,0 +1,26 @@
+# How 5-tapping power launches Emergency Sos
+
+_as of Jan 2021_
+
+Note that the flow is a simplified version of the camera launch flow.
+
+
+### Sequence of events
+
+
+1. [PhoneWindowManager.java](/services/core/java/com/android/server/policy/PhoneWindowManager.java) is responsible for all power button presses (see `interceptPowerKeyDown`).
+2. Even though PWMgr has a lot of logic to detect all manner of power button multipresses and gestures, it also checks with GestureLauncherService, which is also [offered the chance](/services/core/java/com/android/server/policy/PhoneWindowManager.java#943) to [intercept](/services/core/java/com/android/server/GestureLauncherService.java#358) the power key.
+3. GLS is responsible for the emergoncy sos timeout, and if it detects one, it [forwards it to the StatusBarManagerService](/services/core/java/com/android/server/GestureLauncherService.java#475) (which hands it off to SystemUI).
+4. Inside SystemUI, [onEmergencyActionLaunchGestureDetected](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#4039) and determines
+    1. If the gesture is enabled (else do nothing)
+    2. If there is an app to handle the gesture (else do nothing)
+    2. whether the screen is on; if not, we need to delay until that happens
+5. Assuming there is an app, and the setting is one launch Emergengy Flow immediately. [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#4077)
+    1. Note that we cannot have an intent resolver, so we launch the default.
+
+**Which intent launches?**
+
+Due to the nature of the gesture, we need the flow to work behind the lockscreen, and without disambiguation.
+Thus, we always launch the same intent, and verify that there is only one matching intent-filter in the system image.
+
+[The emergengy sos intent action](packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java#36).
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
index 108591b..d097472 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
@@ -15,22 +15,24 @@
   ~ limitations under the License.
   -->
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
-            android:paddingMode="stack">
+            android:paddingMode="stack" >
     <item android:id="@android:id/background"
         android:gravity="center_vertical|fill_horizontal">
-        <layer-list >
+        <layer-list>
             <item>
                 <shape
                     android:tint="?android:attr/colorControlActivated"
                     android:alpha="?android:attr/disabledAlpha">
-                    <size android:height="48dp" />
+                    <size android:height="@dimen/rounded_slider_height" />
                     <solid android:color="@color/white_disabled" />
-                    <corners android:radius="24dp" />
+                    <corners android:radius="@dimen/rounded_slider_corner_radius" />
                 </shape>
             </item>
             <item
-                android:gravity="center_vertical|start"
-                android:start="32dp">
+                android:gravity="center_vertical|left"
+                android:height="@dimen/rounded_slider_icon_size"
+                android:width="@dimen/rounded_slider_icon_size"
+                android:left="@dimen/rounded_slider_icon_inset">
                 <com.android.systemui.util.AlphaTintDrawableWrapper
                     android:drawable="@drawable/ic_brightness"
                     android:tint="?android:attr/colorControlActivated" />
@@ -39,10 +41,8 @@
     </item>
     <item android:id="@android:id/progress"
           android:gravity="center_vertical|fill_horizontal">
-        <clip
-            android:drawable="@drawable/brightness_progress_full_drawable"
-            android:clipOrientation="horizontal"
-            android:gravity="left"
-        />
+            <com.android.systemui.util.RoundedCornerProgressDrawable
+                android:drawable="@drawable/brightness_progress_full_drawable"
+            />
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
index b5def5e..41140a7 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
@@ -15,18 +15,21 @@
   ~ limitations under the License.
   -->
 
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="true">
     <item android:id="@+id/slider_foreground">
         <shape>
-            <size android:height="48dp" />
+            <size android:height="@dimen/rounded_slider_height" />
             <solid android:color="?android:attr/colorControlActivated" />
-            <corners android:radius="24dp"/>
+            <corners android:radius="@dimen/rounded_slider_corner_radius"/>
         </shape>
     </item>
     <item
         android:id="@+id/slider_icon"
-        android:gravity="center_vertical|start"
-        android:start="32dp">
+        android:gravity="center_vertical|right"
+        android:height="@dimen/rounded_slider_icon_size"
+        android:width="@dimen/rounded_slider_icon_size"
+        android:right="@dimen/rounded_slider_icon_inset">
         <com.android.systemui.util.AlphaTintDrawableWrapper
             android:drawable="@drawable/ic_brightness"
             android:tint="?android:attr/colorBackground"
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
index 59dad0e..b8ea622 100644
--- a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
@@ -17,6 +17,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle" >
     <solid
-        android:color="?android:attr/textColorPrimary" />
+        android:color="?android:attr/textColorSecondary" />
     <corners android:radius="2dp" />
 </shape>
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 2a055fc..8f3345f 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -22,82 +22,88 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <ImageView
-        android:id="@+id/preview"
+    <Button
+        android:id="@+id/save"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginVertical="8dp"
-        android:layout_marginHorizontal="48dp"
-        android:adjustViewBounds="true"
-        app:layout_constrainedHeight="true"
-        app:layout_constrainedWidth="true"
-        app:layout_constraintBottom_toBottomOf="@id/guideline"
-        app:layout_constraintEnd_toEndOf="parent"
+        android:text="@string/save"
+        app:layout_constraintEnd_toStartOf="@id/cancel"
+        app:layout_constraintHorizontal_chainStyle="packed"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:background="?android:colorBackground"
-        tools:minHeight="100dp"
-        tools:minWidth="100dp" />
+        app:layout_constraintBottom_toTopOf="@id/guideline" />
 
-    <com.android.systemui.screenshot.CropView
-        android:id="@+id/crop_view"
-        android:visibility="gone"
+    <Button
+        android:id="@+id/cancel"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginVertical="8dp"
-        app:layout_constrainedHeight="true"
-        app:layout_constrainedWidth="true"
-        app:layout_constraintBottom_toBottomOf="@id/guideline"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
+        android:text="@string/cancel"
+        app:layout_constraintEnd_toStartOf="@id/edit"
+        app:layout_constraintStart_toEndOf="@id/save"
         app:layout_constraintTop_toTopOf="parent"
-        app:handleThickness="3dp"
-        app:handleColor="@*android:color/accent_device_default"
-        app:scrimColor="#9444"
-        tools:background="?android:colorBackground"
-        tools:minHeight="100dp"
-        tools:minWidth="100dp" />
+        app:layout_constraintBottom_toTopOf="@id/guideline" />
+
+    <Button
+        android:id="@+id/edit"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/screenshot_edit_label"
+        app:layout_constraintEnd_toStartOf="@id/share"
+        app:layout_constraintStart_toEndOf="@id/cancel"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/guideline" />
+
+    <Button
+        android:id="@+id/share"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@*android:string/share"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/edit"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/guideline" />
 
     <androidx.constraintlayout.widget.Guideline
         android:id="@+id/guideline"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="horizontal"
-        app:layout_constraintGuide_percent="0.9" />
+        app:layout_constraintGuide_percent="0.1" />
 
-    <Button
-        android:id="@+id/close"
+    <ImageView
+        android:id="@+id/preview"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_margin="8dp"
-        android:text="Close"
-        app:layout_constraintEnd_toStartOf="@+id/edit"
-        app:layout_constraintHorizontal_bias="0.5"
-        app:layout_constraintHorizontal_chainStyle="packed"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="@+id/guideline" />
-
-    <Button
-        android:id="@+id/edit"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_margin="8dp"
-        android:text="Edit"
-        app:layout_constraintEnd_toStartOf="@+id/share"
-        app:layout_constraintHorizontal_bias="0.5"
-        app:layout_constraintStart_toEndOf="@+id/close"
-        app:layout_constraintTop_toTopOf="@+id/guideline" />
-
-    <Button
-        android:id="@+id/share"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_margin="8dp"
-        android:text="Share"
+        android:layout_marginBottom="24dp"
+        android:layout_marginHorizontal="48dp"
+        android:adjustViewBounds="true"
+        app:layout_constrainedHeight="true"
+        app:layout_constrainedWidth="true"
+        app:layout_constraintTop_toBottomOf="@id/guideline"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintHorizontal_bias="0.5"
-        app:layout_constraintStart_toEndOf="@+id/edit"
-        app:layout_constraintTop_toTopOf="@+id/guideline" />
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        tools:background="?android:colorBackground"
+        tools:minHeight="100dp"
+        tools:minWidth="100dp" />
+
+    <com.android.systemui.screenshot.CropView
+        android:id="@+id/crop_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="24dp"
+        app:layout_constrainedHeight="true"
+        app:layout_constrainedWidth="true"
+        app:layout_constraintTop_toBottomOf="@id/guideline"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:handleThickness="3dp"
+        app:handleColor="@*android:color/accent_device_default"
+        app:scrimColor="#9444"
+        tools:background="?android:colorBackground"
+        tools:minHeight="100dp"
+        tools:minWidth="100dp" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>
 
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 75f76b4..d6385ff 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -50,18 +50,22 @@
             android:layout="@layout/qs_panel"
             android:layout_width="@dimen/qs_panel_width"
             android:layout_height="match_parent"
-            android:layout_gravity="@integer/notification_panel_layout_gravity"
             android:clipToPadding="false"
             android:clipChildren="false"
-            systemui:viewType="com.android.systemui.plugins.qs.QS" />
+            systemui:viewType="com.android.systemui.plugins.qs.QS"
+            systemui:layout_constraintStart_toStartOf="parent"
+            systemui:layout_constraintEnd_toEndOf="parent"
+        />
 
         <com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
             android:id="@+id/notification_stack_scroller"
             android:layout_marginTop="@dimen/notification_panel_margin_top"
             android:layout_width="@dimen/notification_panel_width"
             android:layout_height="match_parent"
-            android:layout_gravity="@integer/notification_panel_layout_gravity"
-            android:layout_marginBottom="@dimen/close_handle_underlap" />
+            android:layout_marginBottom="@dimen/close_handle_underlap"
+            systemui:layout_constraintStart_toStartOf="parent"
+            systemui:layout_constraintEnd_toEndOf="parent"
+        />
 
         <include layout="@layout/ambient_indication"
             android:id="@+id/ambient_indication_container" />
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 6dff2e3..a6321fe 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -19,4 +19,7 @@
 
     <!-- Max number of columns for quick controls area -->
     <integer name="controls_max_columns">2</integer>
+
+    <!-- Whether to use the split 2-column notification shade -->
+    <bool name="config_use_split_notification_shade">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index dec83d9..722f148 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -39,7 +39,6 @@
         <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
         <item>@string/config_systemUIVendorServiceComponent</item>
         <item>com.android.systemui.SliceBroadcastRelayHandler</item>
-        <item>com.android.systemui.SizeCompatModeActivityController</item>
         <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
         <item>com.android.systemui.toast.ToastUI</item>
         <item>com.android.systemui.wmshell.WMShell</item>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index e2fe223..6c55fb6 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -177,5 +177,9 @@
         <attr name="handleColor" format="color" />
         <attr name="scrimColor" format="color" />
     </declare-styleable>
+
+    <declare-styleable name="RoundedCornerProgressDrawable">
+        <attr name="android:drawable" />
+    </declare-styleable>
 </resources>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 09710d7..bb04c3b 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -301,7 +301,6 @@
         <item>com.android.systemui.ScreenDecorations</item>
         <item>com.android.systemui.biometrics.AuthController</item>
         <item>com.android.systemui.SliceBroadcastRelayHandler</item>
-        <item>com.android.systemui.SizeCompatModeActivityController</item>
         <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
         <item>com.android.systemui.theme.ThemeOverlayController</item>
         <item>com.android.systemui.accessibility.WindowMagnification</item>
@@ -566,4 +565,7 @@
 
     <!-- Whether wallet view is shown in landscape / seascape orientations -->
     <bool name="global_actions_show_landscape_wallet_view">false</bool>
+
+    <!-- Whether to use the split 2-column notification shade -->
+    <bool name="config_use_split_notification_shade">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1fac96b..d92f4ea 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1333,4 +1333,12 @@
     <dimen name="people_space_widget_radius">24dp</dimen>
     <dimen name="people_space_widget_round_radius">100dp</dimen>
     <dimen name="people_space_widget_background_padding">6dp</dimen>
+
+    <dimen name="rounded_slider_height">48dp</dimen>
+    <!-- rounded_slider_height / 2 -->
+    <dimen name="rounded_slider_corner_radius">24dp</dimen>
+    <!-- rounded_slider_height / 2 -->
+    <dimen name="rounded_slider_icon_size">24dp</dimen>
+    <!-- rounded_slider_icon_size / 2 -->
+    <dimen name="rounded_slider_icon_inset">12dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 81b7a7b..d932395 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2816,4 +2816,6 @@
 
     <!-- No translation [CHAR LIMIT=0] -->
     <string name="qs_remove_labels" translatable="false"></string>
+
+    <string name="qs_tile_label_fontFamily" translatable="false">@*android:string/config_headlineFontFamily</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index a74b564..4b04eeb 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -196,7 +196,7 @@
 
     <style name="TextAppearance.QS.TileLabel">
         <item name="android:textSize">@dimen/qs_tile_text_size</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:fontFamily">@string/qs_tile_label_fontFamily</item>
     </style>
 
     <style name="TextAppearance.QS.TileLabel.Secondary">
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
similarity index 67%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
index 19b20f2..e5ced3e 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
-package android.graphics.fonts;
+package com.android.systemui.shared.recents;
 
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/**
+ * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
+ */
+oneway interface ISplitScreenListener {
+    void onStagePositionChanged(int stage, int position);
+    void onTaskStageChanged(int taskId, int stage);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 388eeb6..e38cf23 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shared.recents;
 
+import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
@@ -23,9 +24,11 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.view.MotionEvent;
 
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.systemui.shared.recents.ISplitScreenListener;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteTransitionCompat;
 
@@ -202,4 +205,49 @@
 
     /** Unegisters a RemoteTransitionCompat that will handle transitions. */
     void unregisterRemoteTransition(in RemoteTransitionCompat remoteTransition) = 33;
+
+// SplitScreen APIs...copied from SplitScreen.java
+    /**
+     * Stage position isn't specified normally meaning to use what ever it is currently set to.
+     */
+    //int STAGE_POSITION_UNDEFINED = -1;
+    /**
+     * Specifies that a stage is positioned at the top half of the screen if
+     * in portrait mode or at the left half of the screen if in landscape mode.
+     */
+    //int STAGE_POSITION_TOP_OR_LEFT = 0;
+    /**
+     * Specifies that a stage is positioned at the bottom half of the screen if
+     * in portrait mode or at the right half of the screen if in landscape mode.
+     */
+    //int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
+
+    /**
+     * Stage type isn't specified normally meaning to use what ever the default is.
+     * E.g. exit split-screen and launch the app in fullscreen.
+     */
+    //int STAGE_TYPE_UNDEFINED = -1;
+    /**
+     * The main stage type.
+     * @see MainStage
+     */
+    //int STAGE_TYPE_MAIN = 0;
+    /**
+     * The side stage type.
+     * @see SideStage
+     */
+    //int STAGE_TYPE_SIDE = 1;
+
+    void registerSplitScreenListener(in ISplitScreenListener listener) = 34;
+    void unregisterSplitScreenListener(in ISplitScreenListener listener) = 35;
+
+    /** Hides the side-stage if it is currently visible. */
+    void setSideStageVisibility(in boolean visible) = 36;
+    /** Removes the split-screen stages. */
+    void exitSplitScreen() = 37;
+    void startTask(in int taskId, in int stage, in int position, in Bundle options) = 38;
+    void startShortcut(in String packageName, in String shortcutId, in int stage, in int position,
+            in Bundle options, in UserHandle user) = 39;
+    void startIntent(
+            in PendingIntent intent, in int stage, in int position, in Bundle options) = 40;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 3584c82..e2ca349 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -72,9 +72,13 @@
         return ActivityOptions.makeRemoteTransition(remoteTransition.getTransition());
     }
 
+    /**
+     * Returns ActivityOptions for overriding task transition animation.
+     */
     public static ActivityOptions makeCustomAnimation(Context context, int enterResId,
             int exitResId, final Runnable callback, final Handler callbackHandler) {
-        return ActivityOptions.makeCustomAnimation(context, enterResId, exitResId, callbackHandler,
+        return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId,
+                callbackHandler,
                 new ActivityOptions.OnAnimationStartedListener() {
                     @Override
                     public void onAnimationStarted() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index a56c6a1..e6477f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -18,9 +18,11 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TransitionOldType;
 
 import android.os.RemoteException;
 import android.util.Log;
@@ -65,13 +67,17 @@
             final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
         return new IRemoteAnimationRunner.Stub() {
             @Override
-            public void onAnimationStart(RemoteAnimationTarget[] apps,
+            public void onAnimationStart(@TransitionOldType int transit,
+                    RemoteAnimationTarget[] apps,
                     RemoteAnimationTarget[] wallpapers,
+                    RemoteAnimationTarget[] nonApps,
                     final IRemoteAnimationFinishedCallback finishedCallback) {
                 final RemoteAnimationTargetCompat[] appsCompat =
                         RemoteAnimationTargetCompat.wrap(apps);
                 final RemoteAnimationTargetCompat[] wallpapersCompat =
                         RemoteAnimationTargetCompat.wrap(wallpapers);
+                final RemoteAnimationTargetCompat[] nonAppsCompat =
+                        RemoteAnimationTargetCompat.wrap(nonApps);
                 final Runnable animationFinishedCallback = new Runnable() {
                     @Override
                     public void run() {
@@ -83,8 +89,8 @@
                         }
                     }
                 };
-                remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
-                        animationFinishedCallback);
+                remoteAnimationAdapter.onAnimationStart(transit, appsCompat, wallpapersCompat,
+                        nonAppsCompat, animationFinishedCallback);
             }
 
             @Override
@@ -104,6 +110,9 @@
                         RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
                 final RemoteAnimationTargetCompat[] wallpapersCompat =
                         RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */);
+                // TODO(bc-unlock): Build wrapped object for non-apps target.
+                final RemoteAnimationTargetCompat[] nonAppsCompat =
+                        new RemoteAnimationTargetCompat[0];
                 final Runnable animationFinishedCallback = new Runnable() {
                     @Override
                     public void run() {
@@ -147,7 +156,10 @@
                     }
                 }
                 t.apply();
-                remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
+                // TODO(bc-unlcok): Pass correct transit type.
+                remoteAnimationAdapter.onAnimationStart(
+                        TRANSIT_OLD_NONE,
+                        appsCompat, wallpapersCompat, nonAppsCompat,
                         animationFinishedCallback);
             }
         };
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 33372f6..0076292 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -16,8 +16,11 @@
 
 package com.android.systemui.shared.system;
 
+import android.view.WindowManager;
+
 public interface RemoteAnimationRunnerCompat {
-    void onAnimationStart(RemoteAnimationTargetCompat[] apps,
-            RemoteAnimationTargetCompat[] wallpapers, Runnable finishedCallback);
+    void onAnimationStart(@WindowManager.TransitionOldType int transit,
+            RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers,
+            RemoteAnimationTargetCompat[] nonApps, Runnable finishedCallback);
     void onAnimationCancelled();
 }
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 8d010c7..6c77af7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -19,7 +19,6 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ITaskStackListener;
 import android.content.ComponentName;
-import android.os.IBinder;
 
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -80,7 +79,6 @@
     public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) { }
 
     public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { }
-    public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { }
 
     public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index a907e66..8f08f5a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -18,15 +18,14 @@
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityTaskManager;
-import android.window.TaskSnapshot;
 import android.app.TaskStackListener;
 import android.content.ComponentName;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Trace;
 import android.util.Log;
+import android.window.TaskSnapshot;
 
 import com.android.internal.os.SomeArgs;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -88,13 +87,12 @@
         private static final int ON_TASK_MOVED_TO_FRONT = 14;
         private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 15;
         private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16;
-        private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17;
-        private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18;
-        private static final int ON_TASK_DISPLAY_CHANGED = 19;
-        private static final int ON_TASK_LIST_UPDATED = 20;
-        private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 21;
-        private static final int ON_TASK_DESCRIPTION_CHANGED = 22;
-        private static final int ON_ACTIVITY_ROTATION = 23;
+        private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 17;
+        private static final int ON_TASK_DISPLAY_CHANGED = 18;
+        private static final int ON_TASK_LIST_UPDATED = 19;
+        private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 20;
+        private static final int ON_TASK_DESCRIPTION_CHANGED = 21;
+        private static final int ON_ACTIVITY_ROTATION = 22;
 
         /**
          * List of {@link TaskStackChangeListener} registered from {@link #addListener}.
@@ -248,13 +246,6 @@
         }
 
         @Override
-        public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
-            mHandler.obtainMessage(ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId,
-                    0 /* unused */,
-                    activityToken).sendToTarget();
-        }
-
-        @Override
         public void onTaskDisplayChanged(int taskId, int newDisplayId) {
             mHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
         }
@@ -391,13 +382,6 @@
                         }
                         break;
                     }
-                    case ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED: {
-                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onSizeCompatModeActivityChanged(
-                                    msg.arg1, (IBinder) msg.obj);
-                        }
-                        break;
-                    }
                     case ON_BACK_PRESSED_ON_TASK_ROOT: {
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                             mTaskStackListeners.get(i).onBackPressedOnTaskRoot(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 85e9ca0..276036c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -74,16 +74,7 @@
 
         @Override
         public void onDisplayChanged(int displayId) {
-            if (displayId == DEFAULT_DISPLAY) return;
-            final Presentation presentation = mPresentations.get(displayId);
-            if (presentation != null && mShowing) {
-                hidePresentation(displayId);
-                // update DisplayInfo.
-                final Display display = mDisplayService.getDisplay(displayId);
-                if (display != null) {
-                    showPresentation(display);
-                }
-            }
+
         }
 
         @Override
@@ -305,15 +296,16 @@
         }
 
         @Override
+        public void onDisplayChanged() {
+            updateBounds();
+            getWindow().getDecorView().requestLayout();
+        }
+
+        @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
 
-            final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
-                    .getBounds();
-            mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
-            mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
-            mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
-            mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+            updateBounds();
 
             setContentView(LayoutInflater.from(mContext)
                     .inflate(R.layout.keyguard_presentation, null));
@@ -338,5 +330,14 @@
 
             mKeyguardClockSwitchController.init();
         }
+
+        private void updateBounds() {
+            final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
+                    .getBounds();
+            mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
+            mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
+            mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
+            mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index a7ed672..c182fd1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -297,7 +297,7 @@
     }
 
     private void updateSecurityViewGravity() {
-        View securityView = getChildAt(0);
+        View securityView = findKeyguardSecurityView();
 
         if (securityView == null) {
             return;
@@ -320,7 +320,7 @@
      * by the security view .
      */
     private void updateSecurityViewLocation(boolean animate) {
-        View securityView = getChildAt(0);
+        View securityView = findKeyguardSecurityView();
 
         if (securityView == null) {
             return;
@@ -355,6 +355,23 @@
         }
     }
 
+    @Nullable
+    private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+
+            if (isKeyguardSecurityView(child)) {
+                return (KeyguardSecurityViewFlipper) child;
+            }
+        }
+
+        return null;
+    }
+
+    private boolean isKeyguardSecurityView(View view) {
+        return view instanceof KeyguardSecurityViewFlipper;
+    }
+
     public void onPause() {
         if (mAlertDialog != null) {
             mAlertDialog.dismiss();
@@ -640,39 +657,30 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // This is a little hacky, but this element only ever has one wrap_content child, and is
-        // itself set to match_parent, so we can take a couple of shortcuts compared to
-        // FrameLayout#onMeasure
         int maxHeight = 0;
         int maxWidth = 0;
         int childState = 0;
 
-        int count = getChildCount();
-
         int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                 MeasureSpec.getSize(widthMeasureSpec) / 2,
                 MeasureSpec.getMode(widthMeasureSpec));
 
-        if (count > 1) {
-            throw new IllegalStateException("KeyguardSecurityContainer should only have one child");
-        }
-
-        if (count > 0) {
-            final View securityView = getChildAt(0);
-            if (securityView.getVisibility() != GONE) {
-                if (mOneHandedMode) {
-                    measureChildWithMargins(securityView, halfWidthMeasureSpec, 0,
+        for (int i = 0; i < getChildCount(); i++) {
+            final View view = getChildAt(i);
+            if (view.getVisibility() != GONE) {
+                if (mOneHandedMode && isKeyguardSecurityView(view)) {
+                    measureChildWithMargins(view, halfWidthMeasureSpec, 0,
                             heightMeasureSpec, 0);
                 } else {
-                    measureChildWithMargins(securityView, widthMeasureSpec, 0,
+                    measureChildWithMargins(view, widthMeasureSpec, 0,
                             heightMeasureSpec, 0);
                 }
-                final LayoutParams lp = (LayoutParams) securityView.getLayoutParams();
+                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
                 maxWidth = Math.max(maxWidth,
-                        securityView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+                        view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                 maxHeight = Math.max(maxHeight,
-                        securityView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
-                childState = combineMeasuredStates(childState, securityView.getMeasuredState());
+                        view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+                childState = combineMeasuredStates(childState, view.getMeasuredState());
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
deleted file mode 100644
index 7a52d27..0000000
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui;
-
-import android.content.Context;
-
-import com.android.systemui.dagger.SysUISingleton;
-
-import javax.inject.Inject;
-
-/**
- * Shows a restart-activity button when the foreground activity is in size compatibility mode.
- *
- * // TODO remove this class after cleanup all dependencies.
- * @deprecated Use {@link com.android.wm.shell.sizecompatui.SizeCompatUIController}
- */
-@Deprecated
-@SysUISingleton
-public class SizeCompatModeActivityController extends SystemUI {
-
-    @Inject
-    SizeCompatModeActivityController(Context context) {
-        super(context);
-    }
-
-    @Override
-    public void start() { }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index 68f1414..e07c8403 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -28,7 +28,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.internal.graphics.ColorUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 
@@ -70,9 +69,6 @@
 
     @Override
     public void draw(@NonNull Canvas canvas) {
-        canvas.save();
-        canvas.translate(getPaddingX(), getPaddingY());
-
         final boolean isNightMode = (mContext.getResources().getConfiguration().uiMode
                 & Configuration.UI_MODE_NIGHT_YES) != 0;
         if (!isNightMode) {
@@ -81,8 +77,6 @@
             }
         }
         mFingerprintDrawable.draw(canvas);
-
-        canvas.restore();
     }
 
     @Override
@@ -98,13 +92,7 @@
     @Override
     public void setAlpha(int alpha) {
         super.setAlpha(alpha);
-
-        // Gradually fade into the notification shade color. This needs to be done because the
-        // UDFPS view is drawn on a layer on top of the notification shade
-        final float percent = alpha / 255.f;
-        mSensorPaint.setColor(ColorUtils.blendARGB(mNotificationShadeColor, Color.WHITE, percent));
-        mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0,
-                ColorUtils.blendARGB(mNotificationShadeColor, Color.BLACK, percent));
+        mSensorPaint.setAlpha(alpha);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index fded737..4e3419e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -25,22 +25,21 @@
 import android.view.View;
 
 import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
 
 /**
  * Class that coordinates non-HBM animations (such as enroll, keyguard, BiometricPrompt,
  * FingerprintManager).
  */
 public class UdfpsAnimationView extends View implements DozeReceiver,
-        ScrimController.ScrimChangedListener {
+        StatusBar.ExpansionChangedListener {
 
     private static final String TAG = "UdfpsAnimationView";
 
     @NonNull private UdfpsView mParent;
     @Nullable private UdfpsAnimation mUdfpsAnimation;
     @NonNull private RectF mSensorRect;
-    private int mNotificationPanelAlpha;
-
+    private int mAlpha;
 
     public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -52,12 +51,24 @@
         super.onDraw(canvas);
 
         if (mUdfpsAnimation != null) {
-            final int alpha = mParent.shouldPauseAuth() ? 255 - mNotificationPanelAlpha : 255;
+            final int alpha = mParent.shouldPauseAuth() ? mAlpha : 255;
             mUdfpsAnimation.setAlpha(alpha);
             mUdfpsAnimation.draw(canvas);
         }
     }
 
+    private int expansionToAlpha(float expansion) {
+        // Fade to 0 opacity when reaching this expansion amount
+        final float maxExpansion = 0.4f;
+
+        if (expansion >= maxExpansion) {
+            return 0; // transparent
+        }
+
+        final float percent = expansion / maxExpansion;
+        return (int) ((1 - percent) * 255);
+    }
+
     void setParent(@NonNull UdfpsView parent) {
         mParent = parent;
     }
@@ -87,8 +98,8 @@
     }
 
     @Override
-    public void onAlphaChanged(float alpha) {
-        mNotificationPanelAlpha = (int) (alpha * 255);
+    public void onExpansionChanged(float expansion, boolean expanded) {
+        mAlpha = expansionToAlpha(expansion);
         postInvalidate();
     }
 
@@ -103,4 +114,18 @@
             ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp();
         }
     }
+
+    public int getPaddingX() {
+        if (mUdfpsAnimation == null) {
+            return 0;
+        }
+        return mUdfpsAnimation.getPaddingX();
+    }
+
+    public int getPaddingY() {
+        if (mUdfpsAnimation == null) {
+            return 0;
+        }
+        return mUdfpsAnimation.getPaddingY();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index ac4b93a..c088400 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -45,6 +45,7 @@
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import javax.inject.Inject;
@@ -155,7 +156,7 @@
             WindowManager windowManager,
             @NonNull StatusBarStateController statusBarStateController,
             @Main DelayableExecutor fgExecutor,
-            @NonNull ScrimController scrimController) {
+            @Nullable StatusBar statusBar) {
         mContext = context;
         // The fingerprint manager is queried for UDFPS before this class is constructed, so the
         // fingerprint manager should never be null.
@@ -186,7 +187,7 @@
         mView.setSensorProperties(mSensorProps);
         mView.setHbmCallback(this);
 
-        scrimController.addScrimChangedListener(mView);
+        statusBar.addExpansionChangedListener(mView);
         statusBarStateController.addCallback(mView);
 
         mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController());
@@ -259,15 +260,17 @@
         // Transform dimensions if the device is in landscape mode.
         switch (mContext.getDisplay().getRotation()) {
             case Surface.ROTATION_90:
-                mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
-                mCoreLayoutParams.y =
-                        p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+                mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+                        - paddingX;
+                mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+                        - paddingY;
                 break;
 
             case Surface.ROTATION_270:
-                mCoreLayoutParams.x =
-                        p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
-                mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+                mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+                        - paddingX;
+                mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+                        - paddingY;
                 break;
 
             default:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index d448ed8..7e378d3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -39,14 +39,14 @@
 import com.android.systemui.R;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
 
 /**
  * A view containing 1) A SurfaceView for HBM, and 2) A normal drawable view for all other
  * animations.
  */
 public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator,
-        StatusBarStateController.StateListener, ScrimController.ScrimChangedListener {
+        StatusBarStateController.StateListener, StatusBar.ExpansionChangedListener {
     private static final String TAG = "UdfpsView";
 
     private static final int DEBUG_TEXT_SIZE_PX = 32;
@@ -133,14 +133,18 @@
     }
 
     @Override
-    public void onAlphaChanged(float alpha) {
-        mAnimationView.onAlphaChanged(alpha);
+    public void onExpansionChanged(float expansion, boolean expanded) {
+        mAnimationView.onExpansionChanged(expansion, expanded);
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        mSensorRect.set(0, 0, 2 * mSensorProps.sensorRadius, 2 * mSensorProps.sensorRadius);
+        mSensorRect.set(0 + mAnimationView.getPaddingX(),
+                0 + mAnimationView.getPaddingY(),
+                2 * mSensorProps.sensorRadius + mAnimationView.getPaddingX(),
+                2 * mSensorProps.sensorRadius + mAnimationView.getPaddingY());
+
         mHbmSurfaceView.onSensorRectUpdated(new RectF(mSensorRect));
         mAnimationView.onSensorRectUpdated(new RectF(mSensorRect));
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 55359ea..e5c9d10 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -18,7 +18,6 @@
 
 import com.android.systemui.LatencyTester;
 import com.android.systemui.ScreenDecorations;
-import com.android.systemui.SizeCompatModeActivityController;
 import com.android.systemui.SliceBroadcastRelayHandler;
 import com.android.systemui.SystemUI;
 import com.android.systemui.accessibility.SystemActions;
@@ -113,13 +112,6 @@
     @ClassKey(ShortcutKeyDispatcher.class)
     public abstract SystemUI bindsShortcutKeyDispatcher(ShortcutKeyDispatcher sysui);
 
-    /** Inject into SizeCompatModeActivityController. */
-    @Binds
-    @IntoMap
-    @ClassKey(SizeCompatModeActivityController.class)
-    public abstract SystemUI bindsSizeCompatModeActivityController(
-            SizeCompatModeActivityController sysui);
-
     /** Inject into SliceBroadcastRelayHandler. */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 1b033e9..17f7ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,7 +17,13 @@
 package com.android.systemui.keyguard;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
 
+import android.app.ActivityTaskManager;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Binder;
@@ -26,8 +32,17 @@
 import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.Log;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -43,6 +58,21 @@
     static final String TAG = "KeyguardService";
     static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
 
+    /**
+     * Run Keyguard animation as remote animation in System UI instead of local animation in
+     * the server process.
+     *
+     * Note: Must be consistent with WindowManagerService.
+     */
+    private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+            "persist.wm.enable_remote_keyguard_animation";
+
+    /**
+     * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+     */
+    private static boolean sEnableRemoteKeyguardAnimation =
+            SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
     private final KeyguardViewMediator mKeyguardViewMediator;
     private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
 
@@ -52,6 +82,21 @@
         super();
         mKeyguardViewMediator = keyguardViewMediator;
         mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
+
+        if (sEnableRemoteKeyguardAnimation) {
+            RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+            final RemoteAnimationAdapter exitAnimationAdapter =
+                    new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, exitAnimationAdapter);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+                    exitAnimationAdapter);
+            final RemoteAnimationAdapter occludeAnimationAdapter =
+                    new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, occludeAnimationAdapter);
+            ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
+                    DEFAULT_DISPLAY, definition);
+        }
     }
 
     @Override
@@ -76,6 +121,48 @@
         }
     }
 
+    private final IRemoteAnimationRunner.Stub mExitAnimationRunner =
+            new IRemoteAnimationRunner.Stub() {
+        @Override // Binder interface
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] apps,
+                RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
+                IRemoteAnimationFinishedCallback finishedCallback) {
+            Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
+            checkPermission();
+            mKeyguardViewMediator.startKeyguardExitAnimation(transit, apps, wallpapers,
+                    null /* nonApps */, finishedCallback);
+            Trace.endSection();
+        }
+
+        @Override // Binder interface
+        public void onAnimationCancelled() {
+        }
+    };
+
+    private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner =
+            new IRemoteAnimationRunner.Stub() {
+        @Override // Binder interface
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                       RemoteAnimationTarget[] apps,
+                       RemoteAnimationTarget[] wallpapers,
+                        RemoteAnimationTarget[] nonApps,
+                        IRemoteAnimationFinishedCallback finishedCallback) {
+            // TODO(bc-unlock): Calls KeyguardViewMediator#setOccluded to update the state and
+            // run animation.
+            try {
+                finishedCallback.onAnimationFinished();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "RemoteException");
+            }
+        }
+
+        @Override // Binder interface
+        public void onAnimationCancelled() {
+        }
+    };
+
     private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
 
         @Override // Binder interface
@@ -225,6 +312,11 @@
             mKeyguardViewMediator.onBootCompleted();
         }
 
+        /**
+         * @deprecated When remote animation is enabled, this won't be called anymore. Use
+         * {@code IRemoteAnimationRunner#onAnimationStart} instead.
+         */
+        @Deprecated
         @Override
         public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
             Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e732669..5a918d4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -27,6 +27,9 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
@@ -65,8 +68,13 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseIntArray;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.RemoteAnimationTarget;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
@@ -85,6 +93,7 @@
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Dumpable;
+import com.android.systemui.Interpolators;
 import com.android.systemui.SystemUI;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollector;
@@ -1318,6 +1327,7 @@
             if (mHiding && isOccluded) {
                 // We're in the process of going away but WindowManager wants to show a
                 // SHOW_WHEN_LOCKED activity instead.
+                // TODO(bc-unlock): Migrate to remote animation.
                 startKeyguardExitAnimation(0, 0);
             }
 
@@ -1703,7 +1713,9 @@
                     Trace.beginSection(
                             "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
                     StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
-                    handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
+                    handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration,
+                            params.mApps, params.mWallpapers, params.mNonApps,
+                            params.mFinishedCallback);
                     mFalsingCollector.onSuccessfulUnlock();
                     Trace.endSection();
                     break;
@@ -1990,15 +2002,19 @@
             if (mShowing && !mOccluded) {
                 mKeyguardGoingAwayRunnable.run();
             } else {
+                // TODO(bc-unlock): Fill parameters
                 handleStartKeyguardExitAnimation(
                         SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
-                        mHideAnimation.getDuration());
+                        mHideAnimation.getDuration(), null /* apps */,  null /* wallpapers */,
+                        null /* nonApps */, null /* finishedCallback */);
             }
         }
         Trace.endSection();
     }
 
-    private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+    private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration,
+            RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+            RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
         Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
         if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
                 + " fadeoutDuration=" + fadeoutDuration);
@@ -2031,6 +2047,49 @@
             mWakeAndUnlocking = false;
             mDismissCallbackRegistry.notifyDismissSucceeded();
             mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
+
+            // TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
+            // apps, wallpapers and finishedCallback are set to non-null. nonApps is not yet
+            // supported, so it's always null.
+            mContext.getMainExecutor().execute(() -> {
+                if (finishedCallback == null) {
+                    return;
+                }
+
+                // TODO(bc-unlock): Sample animation, just to apply alpha animation on the app.
+                final SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(
+                        mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+                final RemoteAnimationTarget primary = apps[0];
+                ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+                anim.setDuration(400 /* duration */);
+                anim.setInterpolator(Interpolators.LINEAR);
+                anim.addUpdateListener((ValueAnimator animation) -> {
+                    SurfaceParams params = new SurfaceParams.Builder(primary.leash)
+                            .withAlpha(animation.getAnimatedFraction())
+                            .build();
+                    applier.scheduleApply(params);
+                });
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        try {
+                            finishedCallback.onAnimationFinished();
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "RemoteException");
+                        }
+                    }
+
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        try {
+                            finishedCallback.onAnimationFinished();
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "RemoteException");
+                        }
+                    }
+                });
+                anim.start();
+            });
             resetKeyguardDonePendingLocked();
             mHideAnimationRun = false;
             adjustStatusBarLocked();
@@ -2223,10 +2282,55 @@
         return mKeyguardViewControllerLazy.get();
     }
 
+    /**
+     * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+     * the wallpaper and keyguard flag, and WindowManager has started running keyguard exit
+     * animation.
+     *
+     * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+     * @deprecated Will be migrate to remote animation soon.
+     */
+    @Deprecated
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+        startKeyguardExitAnimation(0, startTime, fadeoutDuration, null, null, null, null);
+    }
+
+    /**
+     * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+     * the wallpaper and keyguard flag, and System UI should start running keyguard exit animation.
+     *
+     * @param apps The list of apps to animate.
+     * @param wallpapers The list of wallpapers to animate.
+     * @param nonApps The list of non-app windows such as Bubbles to animate.
+     * @param finishedCallback The callback to invoke when the animation is finished.
+     */
+    public void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+            RemoteAnimationTarget[] apps,
+            RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+            IRemoteAnimationFinishedCallback finishedCallback) {
+        startKeyguardExitAnimation(transit, 0, 0, apps, wallpapers, nonApps, finishedCallback);
+    }
+
+    /**
+     * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+     * the wallpaper and keyguard flag, and start running keyguard exit animation.
+     *
+     * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+     * @param apps The list of apps to animate.
+     * @param wallpapers The list of wallpapers to animate.
+     * @param nonApps The list of non-app windows such as Bubbles to animate.
+     * @param finishedCallback The callback to invoke when the animation is finished.
+     */
+    private void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+            long startTime, long fadeoutDuration,
+            RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+            RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
         Trace.beginSection("KeyguardViewMediator#startKeyguardExitAnimation");
         Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
-                new StartKeyguardExitAnimParams(startTime, fadeoutDuration));
+                new StartKeyguardExitAnimParams(transit, startTime, fadeoutDuration, apps,
+                        wallpapers, nonApps, finishedCallback));
         mHandler.sendMessage(msg);
         Trace.endSection();
     }
@@ -2300,12 +2404,26 @@
 
     private static class StartKeyguardExitAnimParams {
 
+        @WindowManager.TransitionOldType int mTransit;
         long startTime;
         long fadeoutDuration;
+        RemoteAnimationTarget[] mApps;
+        RemoteAnimationTarget[] mWallpapers;
+        RemoteAnimationTarget[] mNonApps;
+        IRemoteAnimationFinishedCallback mFinishedCallback;
 
-        private StartKeyguardExitAnimParams(long startTime, long fadeoutDuration) {
+        private StartKeyguardExitAnimParams(@WindowManager.TransitionOldType int transit,
+                long startTime, long fadeoutDuration,
+                RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
+                IRemoteAnimationFinishedCallback finishedCallback) {
+            this.mTransit = transit;
             this.startTime = startTime;
             this.fadeoutDuration = fadeoutDuration;
+            this.mApps = apps;
+            this.mWallpapers = wallpapers;
+            this.mNonApps = nonApps;
+            this.mFinishedCallback = finishedCallback;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 4384610..dab4d0b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -666,7 +666,7 @@
     }
 
     private void initSecondaryHomeHandleForRotation() {
-        if (!canShowSecondaryHandle()) {
+        if (mNavBarMode != NAV_BAR_MODE_GESTURAL) {
             return;
         }
 
@@ -1524,7 +1524,7 @@
     }
 
     private boolean canShowSecondaryHandle() {
-        return mNavBarMode == NAV_BAR_MODE_GESTURAL;
+        return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null;
     }
 
     private final Consumer<Integer> mRotationWatcher = rotation -> {
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index a4e9189..580cbcf 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -24,10 +24,8 @@
 import android.app.people.IPeopleManager;
 import android.app.people.PeopleSpaceTile;
 import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
@@ -36,12 +34,9 @@
 import android.util.Log;
 import android.view.ViewGroup;
 
-import androidx.preference.PreferenceManager;
-
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.systemui.R;
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
 
 import java.util.List;
 
@@ -128,26 +123,17 @@
 
     /** Stores the user selected configuration for {@code mAppWidgetId}. */
     private void storeWidgetConfiguration(PeopleSpaceTile tile) {
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-        SharedPreferences.Editor editor = sp.edit();
         if (PeopleSpaceUtils.DEBUG) {
             Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
                     + tile.getId() + " for widget ID: "
                     + mAppWidgetId);
         }
-        // Ensure updates to app widget can be retrieved from both appWidget Id and tile ID.
-        editor.putString(String.valueOf(mAppWidgetId), tile.getId());
-        editor.putInt(tile.getId(), mAppWidgetId);
-        editor.apply();
-        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
-        Bundle options = new Bundle();
-        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, tile);
-        appWidgetManager.updateAppWidgetOptions(mAppWidgetId, options);
-        int[] widgetIds = appWidgetManager.getAppWidgetIds(
-                new ComponentName(mContext, PeopleSpaceWidgetProvider.class));
+
+        PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId);
+        int[] widgetIds = new int[mAppWidgetId];
         // TODO: Populate new widget with existing conversation notification, if there is any.
         PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
-                mNotificationManager);
+                mPeopleManager);
         finishActivity();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 91e7968..994dc6d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -18,6 +18,7 @@
 
 import static android.app.Notification.EXTRA_MESSAGES;
 
+import android.annotation.Nullable;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -70,11 +71,13 @@
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -89,6 +92,13 @@
     private static final int MIN_HOUR = 1;
     private static final int ONE_DAY = 1;
     public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile";
+    public static final String PACKAGE_NAME = "package_name";
+    public static final String USER_ID = "user_id";
+    public static final String SHORTCUT_ID = "shortcut_id";
+
+    public static final String EMPTY_STRING = "";
+    public static final int INVALID_WIDGET_ID = -1;
+    public static final int INVALID_USER_ID = -1;
 
     private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
     private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
@@ -171,70 +181,174 @@
      * notification being posted or removed.
      */
     public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds,
-            AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
-        IPeopleManager peopleManager = IPeopleManager.Stub.asInterface(
-                ServiceManager.getService(Context.PEOPLE_SERVICE));
-        LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+            AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
         Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>();
-        try {
-            List<PeopleSpaceTile> tiles =
-                    PeopleSpaceUtils.getTiles(context, notificationManager,
-                            peopleManager, launcherApps);
-            for (int appWidgetId : appWidgetIds) {
-                String shortcutId = sp.getString(String.valueOf(appWidgetId), null);
-                if (DEBUG) {
-                    Log.d(TAG, "Widget ID: " + appWidgetId + " Shortcut ID: " + shortcutId);
-                }
-
-                Optional<PeopleSpaceTile> entry = tiles.stream().filter(
-                        e -> e.getId().equals(shortcutId)).findFirst();
-
-                if (!entry.isPresent() || shortcutId == null) {
-                    if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
-                    //TODO: Delete app widget id when crash is fixed (b/175486868)
-                    continue;
-                }
-                // Augment current tile based on stored fields.
-                PeopleSpaceTile tile = augmentTileFromStorage(entry.get(), appWidgetManager,
-                        appWidgetId);
-
-                RemoteViews views = createRemoteViews(context, tile, appWidgetId);
-
-                // Tell the AppWidgetManager to perform an update on the current app widget.
-                appWidgetManager.updateAppWidget(appWidgetId, views);
-
-                widgetIdToTile.put(appWidgetId, tile);
+        for (int appWidgetId : appWidgetIds) {
+            PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+                    appWidgetId);
+            if (tile == null) {
+                if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
+                //TODO: Delete app widget id when crash is fixed (b/172932636)
+                continue;
             }
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to retrieve conversations to set tiles: " + e);
+
+            if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
+            RemoteViews views = createRemoteViews(context, tile, appWidgetId);
+
+            // Tell the AppWidgetManager to perform an update on the current app widget.
+            appWidgetManager.updateAppWidget(appWidgetId, views);
+
+            widgetIdToTile.put(appWidgetId, tile);
         }
         getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
     }
 
-    /** Augment {@link PeopleSpaceTile} with fields from stored tile. */
-    @VisibleForTesting
-    static PeopleSpaceTile augmentTileFromStorage(PeopleSpaceTile tile,
-            AppWidgetManager appWidgetManager, int appWidgetId) {
-        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
-        PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
-        if (storedTile == null) {
-            return tile;
+    @Nullable
+    private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
+            AppWidgetManager appWidgetManager,
+            Context context, int appWidgetId) {
+        try {
+            // Migrate storage for existing users.
+            SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+                    Context.MODE_PRIVATE);
+            String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+            int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+            String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
+            if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) {
+                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+                shortcutId = sp.getString(String.valueOf(appWidgetId), null);
+                if (shortcutId == null) {
+                    Log.e(TAG, "Cannot restore widget");
+                    return null;
+                }
+                migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId);
+                pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+                userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+            }
+
+            // Check if tile is cached.
+            Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+            PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+            if (tile != null) {
+                return tile;
+            }
+
+            // If tile is null, we need to retrieve from persisted storage.
+            if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots");
+            LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+            ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
+            if (channel == null) {
+                Log.d(TAG, "Could not retrieve conversation from storage");
+                return null;
+            }
+            return new PeopleSpaceTile.Builder(channel, launcherApps).build();
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
+            return null;
         }
-        return tile.toBuilder()
-                .setBirthdayText(storedTile.getBirthdayText())
-                .setNotificationKey(storedTile.getNotificationKey())
-                .setNotificationContent(storedTile.getNotificationContent())
-                .setNotificationDataUri(storedTile.getNotificationDataUri())
-                .build();
     }
 
-    /** If incoming notification changed tile, store the changes in the tile options. */
-    public static void storeNotificationChange(StatusBarNotification sbn,
+    /** Best-effort attempts to migrate existing users to the new storage format. */
+    // TODO: Remove after sufficient time. Temporary migration storage for existing users.
+    private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
+            int appWidgetId) {
+        try {
+            List<PeopleSpaceTile> tiles =
+                    PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface(
+                            ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
+                            IPeopleManager.Stub.asInterface(
+                                    ServiceManager.getService(Context.PEOPLE_SERVICE)),
+                            context.getSystemService(LauncherApps.class));
+            Optional<PeopleSpaceTile> entry = tiles.stream().filter(
+                    e -> e.getId().equals(shortcutId)).findFirst();
+            if (entry.isPresent()) {
+                if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
+                setStorageForTile(context, entry.get(), appWidgetId);
+            } else {
+                Log.e(TAG, "Could not migrate user");
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Could not query conversations");
+        }
+    }
+
+    /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
+    public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) {
+        // Write relevant persisted storage.
+        SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+                Context.MODE_PRIVATE);
+        SharedPreferences.Editor widgetEditor = widgetSp.edit();
+        widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName());
+        widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId());
+        int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+        widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId);
+        widgetEditor.apply();
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putString(String.valueOf(appWidgetId), tile.getId());
+        String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId);
+        // Don't overwrite existing widgets with the same key.
+        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+        storedWidgetIds.add(String.valueOf(appWidgetId));
+        editor.putStringSet(key, storedWidgetIds);
+        editor.apply();
+
+        // Write cached storage.
+        updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId,
+                tile);
+    }
+
+    /** Removes stored data when tile is deleted. */
+    public static void removeStorageForTile(Context context, int widgetId) {
+        SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
+                Context.MODE_PRIVATE);
+        String packageName = widgetSp.getString(PACKAGE_NAME, null);
+        String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+        int userId = widgetSp.getInt(USER_ID, -1);
+
+        // Delete widgetId mapping to key.
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+        SharedPreferences.Editor editor = sp.edit();
+        String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+        storedWidgetIds.remove(String.valueOf(widgetId));
+        editor.putStringSet(key, storedWidgetIds);
+        editor.remove(String.valueOf(widgetId));
+        editor.apply();
+
+        // Delete all data specifically mapped to widgetId.
+        SharedPreferences.Editor widgetEditor = widgetSp.edit();
+        widgetEditor.remove(PACKAGE_NAME);
+        widgetEditor.remove(USER_ID);
+        widgetEditor.remove(SHORTCUT_ID);
+        widgetEditor.apply();
+    }
+
+    /**
+     * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the
+     * requested update data.
+     */
+    public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId,
+            String packageName, int userId) {
+        SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId),
+                Context.MODE_PRIVATE);
+        String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING);
+        int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID);
+        String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING);
+        return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId)
+                && storedUserId == userId;
+    }
+
+    /**
+     * If incoming notification changed tile, store the changes in the tile options.
+     */
+    public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager,
+            Context context,
+            StatusBarNotification sbn,
             NotificationAction notificationAction, AppWidgetManager appWidgetManager,
             int appWidgetId) {
-        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
-        PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+                appWidgetId);
         if (storedTile == null) {
             if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
             return;
@@ -263,7 +377,7 @@
                     .setNotificationDataUri(null)
                     .build();
         }
-        updateAppWidgetOptions(appWidgetManager, appWidgetId, storedTile);
+        updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
     }
 
     private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
@@ -677,4 +791,23 @@
         }
         return lookupKeysWithBirthdaysToday;
     }
+
+    /**
+     * Returns the uniquely identifying key for the conversation.
+     *
+     * <p>{@code userId} will always be a number, so we put user ID as the
+     * delimiter between the app-provided strings of shortcut ID and package name.
+     *
+     * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
+     * a {@code packageName} to always start with a letter. This restriction means we are
+     * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
+     * case is impossible given the package name restrictions:
+     * <ul>
+     *     <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
+     *     <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
+     * </ul>
+     */
+    public static String getKey(String shortcutId, String packageName, int userId) {
+        return shortcutId + "/" + userId + "/" + packageName;
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index ad6046b..bee54e4 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.people.widget;
 
-import android.app.INotificationManager;
 import android.app.NotificationChannel;
+import android.app.people.IPeopleManager;
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -29,6 +29,7 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
+import android.widget.RemoteViews;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.appwidget.IAppWidgetService;
@@ -37,7 +38,8 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 
-import java.util.Objects;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -51,7 +53,7 @@
     private final Context mContext;
     private IAppWidgetService mAppWidgetService;
     private AppWidgetManager mAppWidgetManager;
-    private INotificationManager mNotificationManager;
+    private IPeopleManager mPeopleManager;
 
     @Inject
     public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
@@ -59,11 +61,13 @@
         mContext = context;
         mAppWidgetService = appWidgetService;
         mAppWidgetManager = AppWidgetManager.getInstance(context);
-        mNotificationManager = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mPeopleManager = IPeopleManager.Stub.asInterface(
+                ServiceManager.getService(Context.PEOPLE_SERVICE));
     }
 
-    /** Constructor used for testing. */
+    /**
+     * Constructor used for testing.
+     */
     @VisibleForTesting
     protected PeopleSpaceWidgetManager(Context context) {
         if (DEBUG) Log.d(TAG, "constructor");
@@ -72,16 +76,20 @@
                 ServiceManager.getService(Context.APPWIDGET_SERVICE));
     }
 
-    /** AppWidgetManager setter used for testing. */
+    /**
+     * AppWidgetManager setter used for testing.
+     */
     @VisibleForTesting
     protected void setAppWidgetManager(IAppWidgetService appWidgetService,
-            AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
+            AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
         mAppWidgetService = appWidgetService;
         mAppWidgetManager = appWidgetManager;
-        mNotificationManager = notificationManager;
+        mPeopleManager = peopleManager;
     }
 
-    /** Updates People Space widgets. */
+    /**
+     * Updates People Space widgets.
+     */
     public void updateWidgets() {
         try {
             if (DEBUG) Log.d(TAG, "updateWidgets called");
@@ -99,7 +107,7 @@
 
             if (showSingleConversation) {
                 PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
-                        mAppWidgetManager, mNotificationManager);
+                        mAppWidgetManager, mPeopleManager);
             } else {
                 mAppWidgetService
                         .notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
@@ -114,30 +122,38 @@
      * Check if any existing People tiles match the incoming notification change, and store the
      * change in the tile if so.
      */
-    public void storeNotificationChange(StatusBarNotification sbn,
+    public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
             PeopleSpaceUtils.NotificationAction notificationAction) {
-        if (DEBUG) Log.d(TAG, "storeNotificationChange called");
+        RemoteViews views = new RemoteViews(
+                mContext.getPackageName(), R.layout.people_space_small_avatar_tile);
+        if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
         boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
         if (!showSingleConversation) {
             return;
         }
         try {
+            String sbnShortcutId = sbn.getShortcutId();
+            if (sbnShortcutId == null) {
+                return;
+            }
             int[] widgetIds = mAppWidgetService.getAppWidgetIds(
                     new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
             );
             if (widgetIds.length == 0) {
                 return;
             }
-
             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-            for (int widgetId : widgetIds) {
-                String shortcutId = sp.getString(String.valueOf(widgetId), null);
-                if (!Objects.equals(sbn.getShortcutId(), shortcutId)) {
-                    continue;
-                }
+            int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier();
+            String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
+            Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+            if (storedWidgetIds.isEmpty()) {
+                return;
+            }
+            for (String widgetIdString : storedWidgetIds) {
+                int widgetId = Integer.parseInt(widgetIdString);
                 if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
-                PeopleSpaceUtils.storeNotificationChange(
+                PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext,
                         sbn, notificationAction, mAppWidgetManager, widgetId);
             }
         } catch (Exception e) {
@@ -159,8 +175,7 @@
         public void onNotificationPosted(
                 StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
             if (DEBUG) Log.d(TAG, "onNotificationPosted");
-            storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
-            updateWidgets();
+            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
         }
 
         @Override
@@ -169,8 +184,7 @@
                 NotificationListenerService.RankingMap rankingMap
         ) {
             if (DEBUG) Log.d(TAG, "onNotificationRemoved");
-            storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
-            updateWidgets();
+            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
         }
 
         @Override
@@ -179,8 +193,7 @@
                 NotificationListenerService.RankingMap rankingMap,
                 int reason) {
             if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason);
-            storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
-            updateWidgets();
+            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
         }
 
         @Override
@@ -207,4 +220,4 @@
         }
     };
 
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index 7f204cc..f5577d3 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.people.widget;
 
-import android.app.INotificationManager;
 import android.app.PendingIntent;
+import android.app.people.IPeopleManager;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProvider;
 import android.content.Context;
@@ -53,8 +53,8 @@
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
         if (showSingleConversation) {
             PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds,
-                    appWidgetManager, INotificationManager.Stub.asInterface(
-                            ServiceManager.getService(Context.NOTIFICATION_SERVICE)));
+                    appWidgetManager, IPeopleManager.Stub.asInterface(
+                            ServiceManager.getService(Context.PEOPLE_SERVICE)));
             return;
         }
         // Perform this loop procedure for each App Widget that belongs to this provider
@@ -91,6 +91,7 @@
         for (int widgetId : appWidgetIds) {
             if (DEBUG) Log.d(TAG, "Widget removed");
             mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
+            PeopleSpaceUtils.removeStorageForTile(context, widgetId);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index e9207f1..d248ab5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -29,6 +29,8 @@
 import com.android.systemui.qs.TouchAnimator.Builder;
 import com.android.systemui.qs.TouchAnimator.Listener;
 import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.qs.tileimpl.QSTileBaseView;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
@@ -87,11 +89,13 @@
     private final Executor mExecutor;
     private final TunerService mTunerService;
     private boolean mShowCollapsedOnKeyguard;
+    private final FeatureFlags mFeatureFlags;
 
     @Inject
     public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanelController qsPanelController,
             QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
-            QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService) {
+            QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService,
+            FeatureFlags featureFlags) {
         mQs = qs;
         mQuickQsPanel = quickPanel;
         mQsPanelController = qsPanelController;
@@ -100,6 +104,7 @@
         mHost = qsTileHost;
         mExecutor = executor;
         mTunerService = tunerService;
+        mFeatureFlags = featureFlags;
         mHost.addCallback(this);
         mQsPanelController.addOnAttachStateChangeListener(this);
         qs.getView().addOnLayoutChangeListener(this);
@@ -228,6 +233,7 @@
                 // Quick tiles.
                 QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
                 if (quickTileView == null) continue;
+                View qqsBgCircle = ((QSTileBaseView) quickTileView).getBgCircle();
 
                 getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
                 getRelativePosition(loc2, tileIcon, view);
@@ -249,6 +255,11 @@
                     translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
                     translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
 
+                    if (mFeatureFlags.isQSLabelsEnabled()) {
+                        firstPageBuilder.addFloat(qqsBgCircle, "alpha", 1, 1, 0);
+                        mAllViews.add(qqsBgCircle);
+                    }
+
                 } else { // These tiles disappear when expanding
                     firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
                     translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index d0601f0..91ae571 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -777,10 +777,6 @@
         updatePadding();
     }
 
-    boolean useSideLabels() {
-        return mSideLabels;
-    }
-
     private class H extends Handler {
         private static final int SHOW_DETAIL = 1;
         private static final int SET_TILE_VISIBILITY = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 30774be..f56a890 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -19,7 +19,6 @@
 import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
 import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
 import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
-import static com.android.systemui.qs.dagger.QSFlagsModule.QS_SIDE_LABELS;
 import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
 
 import android.annotation.NonNull;
@@ -93,8 +92,7 @@
             DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
             QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
             BrightnessSlider.Factory brightnessSliderFactory,
-            @Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
-            @Named(QS_SIDE_LABELS) boolean useSideLabels) {
+            @Named(QS_LABELS_FLAG) boolean qsLabelsFlag) {
         super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
                 metricsLogger, uiEventLogger, qsLogger, dumpManager);
         mQsSecurityFooter = qsSecurityFooter;
@@ -110,7 +108,7 @@
         mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider);
 
         mQsLabelsFlag = qsLabelsFlag;
-        mSideLabels = useSideLabels;
+        mSideLabels = qsLabelsFlag;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index e2d7d20..a0db200 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs;
 
 import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
+import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
 import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
 
 import com.android.internal.logging.MetricsLogger;
@@ -40,6 +41,8 @@
 @QSScope
 public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> {
 
+    private boolean mUseSideLabels;
+
     private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
             newConfig -> {
                 int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
@@ -54,9 +57,10 @@
             @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
             @Named(QUICK_QS_PANEL) MediaHost mediaHost,
             MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
-            DumpManager dumpManager) {
+            DumpManager dumpManager, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag) {
         super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
                 uiEventLogger, qsLogger, dumpManager);
+        mUseSideLabels = qsLabelsFlag;
     }
 
     @Override
@@ -97,7 +101,7 @@
                 break;
             }
         }
-        if (mView.useSideLabels()) {
+        if (mUseSideLabels) {
             List<QSTile> newTiles = new ArrayList<>();
             for (int i = 0; i < tiles.size(); i += 2) {
                 newTiles.add(tiles.get(i));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
index ad4c8bb..9ab2d73 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
@@ -18,7 +18,6 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.util.settings.SecureSettings;
 
 import javax.inject.Named;
 
@@ -28,7 +27,6 @@
 @Module
 public interface QSFlagsModule {
     String QS_LABELS_FLAG = "qs_labels_flag";
-    String QS_SIDE_LABELS = "qs_side_labels";
 
     @Provides
     @SysUISingleton
@@ -36,12 +34,4 @@
     static boolean provideQSFlag(FeatureFlags featureFlags) {
         return featureFlags.isQSLabelsEnabled();
     }
-
-    @Provides
-    @SysUISingleton
-    @Named(QS_SIDE_LABELS)
-    static boolean provideSideLabels(SecureSettings secureSettings,
-            @Named(QS_LABELS_FLAG) boolean qsLabels) {
-        return qsLabels && secureSettings.getInt("sysui_side_labels", 0) != 0;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 9e582dd..11e6330 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -14,7 +14,7 @@
 
 package com.android.systemui.qs.tileimpl;
 
-import static com.android.systemui.qs.dagger.QSFlagsModule.QS_SIDE_LABELS;
+import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
 
 import android.content.Context;
 import android.os.Build;
@@ -98,7 +98,7 @@
     @Inject
     public QSFactoryImpl(
             Lazy<QSHost> qsHostLazy,
-            @Named(QS_SIDE_LABELS) boolean useSideLabels,
+            @Named(QS_LABELS_FLAG) boolean useSideLabels,
             Provider<CustomTile.Builder> customTileBuilderProvider,
             Provider<WifiTile> wifiTileProvider,
             Provider<InternetTile> internetTileProvider,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 2dbd2cf..a699e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -36,7 +36,7 @@
 
 /** View that represents a standard quick settings tile. **/
 public class QSTileView extends QSTileBaseView {
-    private static final int MAX_LABEL_LINES = 2;
+    protected int mMaxLabelLines = 2;
     private View mDivider;
     protected TextView mLabel;
     protected TextView mSecondLine;
@@ -109,10 +109,17 @@
 
         // Remeasure view if the primary label requires more then 2 lines or the secondary label
         // text will be cut off.
-        if (mLabel.getLineCount() > MAX_LABEL_LINES || !TextUtils.isEmpty(mSecondLine.getText())
+        if (mLabel.getLineCount() > mMaxLabelLines || !TextUtils.isEmpty(mSecondLine.getText())
                         && mSecondLine.getLineHeight() > mSecondLine.getHeight()) {
-            mLabel.setSingleLine();
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            if (!mLabel.isSingleLine()) {
+                mLabel.setSingleLine();
+                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            }
+        } else {
+            if (mLabel.isSingleLine()) {
+                mLabel.setSingleLine(false);
+                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index 2ef78c2..dc81b702 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -24,7 +24,6 @@
 import android.graphics.drawable.RippleDrawable
 import android.service.quicksettings.Tile.STATE_ACTIVE
 import android.view.Gravity
-import android.view.LayoutInflater
 import android.view.View
 import android.widget.LinearLayout
 import com.android.systemui.R
@@ -42,38 +41,23 @@
 
     init {
         orientation = HORIZONTAL
-        mDualTargetAllowed = true
+        mDualTargetAllowed = false
         mBg.setImageDrawable(null)
-        createDivider()
         mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
+        mMaxLabelLines = 3
     }
 
     override fun createLabel() {
         super.createLabel()
         findViewById<LinearLayout>(R.id.label_group)?.gravity = Gravity.START
         mLabel.gravity = Gravity.START
+        mLabel.textDirection = TEXT_DIRECTION_LOCALE
         mSecondLine.gravity = Gravity.START
+        mSecondLine.textDirection = TEXT_DIRECTION_LOCALE
         val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
-        mLabelContainer.setPadding(padding, padding, padding, padding)
-        (mLabelContainer.layoutParams as LayoutParams).gravity = Gravity.CENTER_VERTICAL
-    }
-
-    fun createDivider() {
-        divider = LayoutInflater.from(context).inflate(R.layout.qs_tile_label_divider, this, false)
-        val position = indexOfChild(mLabelContainer)
-        addView(divider, position)
-    }
-
-    override fun init(
-        click: OnClickListener?,
-        secondaryClick: OnClickListener?,
-        longClick: OnLongClickListener?
-    ) {
-        super.init(click, secondaryClick, longClick)
-        mLabelContainer.setOnClickListener {
-            longClick?.onLongClick(it)
-        }
-        mLabelContainer.isClickable = false
+        mLabelContainer.setPaddingRelative(0, padding, padding, padding)
+        (mLabelContainer.layoutParams as LayoutParams).gravity =
+                Gravity.CENTER_VERTICAL or Gravity.START
     }
 
     override fun updateRippleSize() {
@@ -83,7 +67,7 @@
         val d = super.newTileBackground()
         if (paintDrawable == null) {
             paintDrawable = PaintDrawable(Color.WHITE).apply {
-                setCornerRadius(30f)
+                setCornerRadius(50f)
             }
         }
         if (d is RippleDrawable) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 760ebad..a9f76f6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -35,6 +35,7 @@
 
 import android.annotation.FloatRange;
 import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -83,6 +84,7 @@
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.systemui.shared.recents.ISplitScreenListener;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -98,6 +100,7 @@
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.FileDescriptor;
@@ -133,7 +136,8 @@
     private final Context mContext;
     private final Optional<Pip> mPipOptional;
     private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
-    private final Optional<LegacySplitScreen> mSplitScreenOptional;
+    private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
+    private final Optional<SplitScreen> mSplitScreenOptional;
     private SysUiState mSysUiState;
     private final Handler mHandler;
     private final Lazy<NavigationBarController> mNavBarControllerLazy;
@@ -263,7 +267,7 @@
             }
             final long token = Binder.clearCallingIdentity();
             try {
-                return mSplitScreenOptional.map(splitScreen ->
+                return mLegacySplitScreenOptional.map(splitScreen ->
                         splitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds())
                         .orElse(null);
             } finally {
@@ -401,7 +405,7 @@
 
         @Override
         public void setSplitScreenMinimized(boolean minimized) {
-            mSplitScreenOptional.ifPresent(
+            mLegacySplitScreenOptional.ifPresent(
                     splitScreen -> splitScreen.setMinimized(minimized));
         }
 
@@ -559,6 +563,105 @@
             }
         }
 
+        @Override
+        public void registerSplitScreenListener(ISplitScreenListener listener) {
+            if (!verifyCaller("registerSplitScreenListener")) {
+                return;
+            }
+            mISplitScreenListener = listener;
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mSplitScreenOptional.ifPresent(
+                        s -> s.registerSplitScreenListener(mSplitScreenListener));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void unregisterSplitScreenListener(ISplitScreenListener listener) {
+            if (!verifyCaller("unregisterSplitScreenListener")) {
+                return;
+            }
+            mISplitScreenListener = null;
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mSplitScreenOptional.ifPresent(
+                        s -> s.unregisterSplitScreenListener(mSplitScreenListener));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setSideStageVisibility(boolean visible) {
+            if (!verifyCaller("setSideStageVisibility")) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mSplitScreenOptional.ifPresent(s -> s.setSideStageVisibility(visible));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void exitSplitScreen() {
+            if (!verifyCaller("exitSplitScreen")) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mSplitScreenOptional.ifPresent(s -> s.exitSplitScreen());
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void startTask(int taskId, int stage, int position, Bundle options) {
+            if (!verifyCaller("startTask")) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mSplitScreenOptional.ifPresent(
+                        s -> s.startTask(taskId, stage, position, options));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void startShortcut(String packageName, String shortcutId, int stage, int position,
+                Bundle options, UserHandle user) {
+            if (!verifyCaller("startShortcut")) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mSplitScreenOptional.ifPresent(s ->
+                        s.startShortcut(packageName, shortcutId, stage, position, options, user));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void startIntent(PendingIntent intent, int stage, int position, Bundle options) {
+            if (!verifyCaller("startIntent")) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mSplitScreenOptional.ifPresent(s ->
+                        s.startIntent(intent, stage, position, options));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         private boolean verifyCaller(String reason) {
             final int callerId = Binder.getCallingUserHandle().getIdentifier();
             if (callerId != mCurrentBoundedUserId) {
@@ -658,6 +761,32 @@
     private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
             = this::cleanupAfterDeath;
 
+    private ISplitScreenListener mISplitScreenListener;
+    private final SplitScreen.SplitScreenListener mSplitScreenListener =
+            new SplitScreen.SplitScreenListener() {
+        @Override
+        public void onStagePositionChanged(int stage, int position) {
+            try {
+                if (mISplitScreenListener != null) {
+                    mISplitScreenListener.onStagePositionChanged(stage, position);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG_OPS, "onStagePositionChanged", e);
+            }
+        }
+
+        @Override
+        public void onTaskStageChanged(int taskId, int stage) {
+            try {
+                if (mISplitScreenListener != null) {
+                    mISplitScreenListener.onTaskStageChanged(taskId, stage);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG_OPS, "onTaskStageChanged", e);
+            }
+        }
+    };
+
     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
     @Inject
     public OverviewProxyService(Context context, CommandQueue commandQueue,
@@ -665,7 +794,8 @@
             NavigationModeController navModeController,
             NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
             Optional<Pip> pipOptional,
-            Optional<LegacySplitScreen> splitScreenOptional,
+            Optional<LegacySplitScreen> legacySplitScreenOptional,
+            Optional<SplitScreen> splitScreenOptional,
             Optional<Lazy<StatusBar>> statusBarOptionalLazy,
             Optional<OneHanded> oneHandedOptional,
             BroadcastDispatcher broadcastDispatcher,
@@ -718,9 +848,10 @@
         });
         mCommandQueue = commandQueue;
 
-        splitScreenOptional.ifPresent(splitScreen ->
-                splitScreen.registerBoundsChangeListener(mSplitScreenBoundsChangeListener));
         mSplitScreenOptional = splitScreenOptional;
+        legacySplitScreenOptional.ifPresent(splitScreen ->
+                splitScreen.registerBoundsChangeListener(mSplitScreenBoundsChangeListener));
+        mLegacySplitScreenOptional = legacySplitScreenOptional;
 
         // Listen for user setup
         startTracking();
@@ -835,7 +966,7 @@
         startConnectionToCurrentUser();
 
         // Clean up the minimized state if launcher dies
-        mSplitScreenOptional.ifPresent(
+        mLegacySplitScreenOptional.ifPresent(
                 splitScreen -> splitScreen.setMinimized(false));
 
         // Clean up any registered remote transitions
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
index 8ff66f5..20f8451 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
@@ -108,6 +108,15 @@
     }
 
     Bitmap toBitmap() {
+        return toBitmap(new Rect(0, 0, getWidth(), getHeight()));
+    }
+
+    /**
+     * @param bounds Selected portion of the tile set's bounds (equivalent to tile bounds coord
+     *               space). For example, to get the whole doc, use Rect(0, 0, getWidth(),
+     *               getHeight()).
+     */
+    Bitmap toBitmap(Rect bounds) {
         if (mTiles.isEmpty()) {
             return null;
         }
@@ -115,6 +124,9 @@
         output.setPosition(0, 0, getWidth(), getHeight());
         RecordingCanvas canvas = output.beginRecording();
         canvas.translate(-getLeft(), -getTop());
+        // Additional translation to account for the requested bounds
+        canvas.translate(-bounds.left, -bounds.top);
+        canvas.clipRect(bounds);
         for (ImageTile tile : mTiles) {
             canvas.save();
             canvas.translate(tile.getLeft(), tile.getTop());
@@ -122,7 +134,7 @@
             canvas.restore();
         }
         output.endRecording();
-        return HardwareRenderer.createHardwareBitmap(output, getWidth(), getHeight());
+        return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height());
     }
 
     int getLeft() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 3bc5ebf..5c650ad 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -325,8 +325,6 @@
             Log.d(TAG, "createAnim: bounds=" + bounds + " showFlash=" + showFlash);
         }
 
-        Rect previewBounds = new Rect();
-        mScreenshotPreview.getBoundsOnScreen(previewBounds);
         Rect targetPosition = new Rect();
         mScreenshotPreview.getHitRect(targetPosition);
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index db29533..18c379a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -54,7 +55,8 @@
     // TODO: Support saving without additional action.
     private enum PendingAction {
         SHARE,
-        EDIT
+        EDIT,
+        SAVE
     }
 
     public static final int MAX_PAGES = 5;
@@ -74,9 +76,11 @@
     private RequestCallback mCallback;
     private Window mWindow;
     private ImageView mPreview;
-    private View mClose;
+    private View mSave;
+    private View mCancel;
     private View mEdit;
     private View mShare;
+    private CropView mCropView;
 
     public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
             Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) {
@@ -111,11 +115,14 @@
                 .addOnComputeInternalInsetsListener(this);
         mPreview = findViewById(R.id.preview);
 
-        mClose = findViewById(R.id.close);
+        mSave = findViewById(R.id.save);
+        mCancel = findViewById(R.id.cancel);
         mEdit = findViewById(R.id.edit);
         mShare = findViewById(R.id.share);
+        mCropView = findViewById(R.id.crop_view);
 
-        mClose.setOnClickListener(this::onClicked);
+        mSave.setOnClickListener(this::onClicked);
+        mCancel.setOnClickListener(this::onClicked);
         mEdit.setOnClickListener(this::onClicked);
         mShare.setOnClickListener(this::onClicked);
 
@@ -130,7 +137,8 @@
     }
 
     void disableButtons() {
-        mClose.setEnabled(false);
+        mSave.setEnabled(false);
+        mCancel.setEnabled(false);
         mEdit.setEnabled(false);
         mShare.setEnabled(false);
     }
@@ -139,19 +147,17 @@
         Log.d(TAG, "button clicked!");
 
         int id = v.getId();
-        if (id == R.id.close) {
-            v.setPressed(true);
-            disableButtons();
+        v.setPressed(true);
+        disableButtons();
+        if (id == R.id.save) {
+            startExport(PendingAction.SAVE);
+        } else if (id == R.id.cancel) {
             doFinish();
         } else if (id == R.id.edit) {
             mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT);
-            v.setPressed(true);
-            disableButtons();
             startExport(PendingAction.EDIT);
         } else if (id == R.id.share) {
             mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE);
-            v.setPressed(true);
-            disableButtons();
             startExport(PendingAction.SHARE);
         }
     }
@@ -165,8 +171,13 @@
     }
 
     private void startExport(PendingAction action) {
+        Rect croppedPortion = new Rect(
+                0,
+                (int) (mImageTileSet.getHeight() * mCropView.getTopBoundary()),
+                mImageTileSet.getWidth(),
+                (int) (mImageTileSet.getHeight() * mCropView.getBottomBoundary()));
         ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
-                mBgExecutor, mRequestId, mImageTileSet.toBitmap(), mCaptureTime);
+                mBgExecutor, mRequestId, mImageTileSet.toBitmap(croppedPortion), mCaptureTime);
         exportFuture.addListener(() -> {
             try {
                 ImageExporter.Result result = exportFuture.get();
@@ -191,24 +202,21 @@
         }
         intent.setType("image/png");
         intent.setData(uri);
-        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        Intent sharingChooserIntent = Intent.createChooser(intent, null)
-                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
-                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
+                | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
 
-        mContext.startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
+        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
     }
 
     private void doShare(Uri uri) {
         Intent intent = new Intent(Intent.ACTION_SEND);
         intent.setType("image/png");
         intent.setData(uri);
-        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
+                | Intent.FLAG_GRANT_READ_URI_PERMISSION);
         Intent sharingChooserIntent = Intent.createChooser(intent, null)
-                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
-                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
         mContext.startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
index 8dcc8b4..3c7d78c 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
@@ -18,7 +18,6 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.util.settings.SecureSettings;
 
 import javax.inject.Inject;
 
@@ -30,25 +29,21 @@
 
     private static final String THICK_BRIGHTNESS_SLIDER = "sysui_thick_brightness";
     private final FeatureFlags mFeatureFlags;
-    private final boolean mUseThickSlider;
-    private final boolean mUseMirrorOnThickSlider;
 
     @Inject
-    public BrightnessControllerSettings(SecureSettings settings, FeatureFlags featureFlags) {
+    public BrightnessControllerSettings(FeatureFlags featureFlags) {
         mFeatureFlags = featureFlags;
-        mUseThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 0;
-        mUseMirrorOnThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 2;
     }
 
     // Changing this setting between zero and non-zero may crash systemui down the line. Better to
     // restart systemui after changing it.
     /** */
     boolean useThickSlider() {
-        return mUseThickSlider && mFeatureFlags.useNewBrightnessSlider();
+        return mFeatureFlags.useNewBrightnessSlider();
     }
 
     /** */
     boolean useMirrorOnThickSlider() {
-        return !useThickSlider() || (useThickSlider() && mUseMirrorOnThickSlider);
+        return !useThickSlider();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
index 53ff1df..a6aec3b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
@@ -17,7 +17,6 @@
 package com.android.systemui.settings.brightness;
 
 import android.content.Context;
-import android.graphics.drawable.ClipDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.view.LayoutInflater;
@@ -33,6 +32,7 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.util.RoundedCornerProgressDrawable;
 import com.android.systemui.util.ViewController;
 
 import javax.inject.Inject;
@@ -292,8 +292,8 @@
             if (b.getProgressDrawable() instanceof LayerDrawable) {
                 Drawable progress = ((LayerDrawable) b.getProgressDrawable())
                         .findDrawableByLayerId(com.android.internal.R.id.progress);
-                if (progress instanceof ClipDrawable) {
-                    Drawable inner = ((ClipDrawable) progress).getDrawable();
+                if (progress instanceof RoundedCornerProgressDrawable) {
+                    Drawable inner = ((RoundedCornerProgressDrawable) progress).getDrawable();
                     if (inner instanceof LayerDrawable) {
                         return (LayerDrawable) inner;
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 88b9c6c..d08f973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -33,6 +33,7 @@
 import android.view.SyncRtSurfaceTransactionApplier;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.View;
+import android.view.WindowManager;
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.policy.ScreenDecorationsUtils;
@@ -159,8 +160,10 @@
         }
 
         @Override
-        public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] remoteAnimationTargets,
                 RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
+                RemoteAnimationTarget[] remoteAnimationNonAppTargets,
                 IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
                     throws RemoteException {
             mMainExecutor.execute(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 175e046..a516742 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -45,7 +45,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
-import android.widget.FrameLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
@@ -847,11 +846,14 @@
         return mView.getChildAtRawPosition(x, y);
     }
 
-    public FrameLayout.LayoutParams getLayoutParams() {
-        return (FrameLayout.LayoutParams) mView.getLayoutParams();
+    public ViewGroup.LayoutParams getLayoutParams() {
+        return mView.getLayoutParams();
     }
 
-    public void setLayoutParams(FrameLayout.LayoutParams lp) {
+    /**
+     * Updates layout parameters on the root view
+     */
+    public void setLayoutParams(ViewGroup.LayoutParams lp) {
         mView.setLayoutParams(lp);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index e6efba7..5d2203b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -286,8 +286,7 @@
         float currentYPosition = -algorithmState.scrollY;
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
-            currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition,
-                    false /* reverse */);
+            currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
         }
     }
 
@@ -301,10 +300,6 @@
      * @param currentYPosition The Y position of the current pass of the algorithm.  For a forward
      *                         pass, this should be the top of the child; for a reverse pass, the
      *                         bottom of the child.
-     * @param reverse          Whether we're laying out children in the reverse direction (Y
-     *                         positions
-     *                         decreasing) instead of the forward direction (Y positions
-     *                         increasing).
      * @return The Y position after laying out the child.  This will be the {@code currentYPosition}
      * for the next call to this method, after adjusting for any gaps between children.
      */
@@ -312,8 +307,7 @@
             int i,
             StackScrollAlgorithmState algorithmState,
             AmbientState ambientState,
-            float currentYPosition,
-            boolean reverse) {
+            float currentYPosition) {
         ExpandableView child = algorithmState.visibleChildren.get(i);
         ExpandableView previousChild = i > 0 ? algorithmState.visibleChildren.get(i - 1) : null;
         final boolean applyGapHeight =
@@ -323,20 +317,12 @@
         ExpandableViewState childViewState = child.getViewState();
         childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
 
-        if (applyGapHeight && !reverse) {
+        if (applyGapHeight) {
             currentYPosition += mGapHeight;
         }
-
         int childHeight = getMaxAllowedChildHeight(child);
-        if (reverse) {
-            childViewState.yTranslation = currentYPosition
-                    - (childHeight + mPaddingBetweenElements);
-            if (currentYPosition <= 0) {
-                childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
-            }
-        } else {
-            childViewState.yTranslation = currentYPosition;
-        }
+        childViewState.yTranslation = currentYPosition;
+
         boolean isFooterView = child instanceof FooterView;
         boolean isEmptyShadeView = child instanceof EmptyShadeView;
 
@@ -362,16 +348,9 @@
             clampPositionToShelf(child, childViewState, ambientState);
         }
 
-        if (reverse) {
-            currentYPosition = childViewState.yTranslation;
-            if (applyGapHeight) {
-                currentYPosition -= mGapHeight;
-            }
-        } else {
-            currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
-            if (currentYPosition <= 0) {
-                childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
-            }
+        currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
+        if (currentYPosition <= 0) {
+            childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
         }
         if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) {
             Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index cf4e9be..3b47594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -95,6 +95,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.qs.QSDetailDisplayer;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -292,6 +293,7 @@
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
     private final QSDetailDisplayer mQSDetailDisplayer;
+    private final FeatureFlags mFeatureFlags;
     private final ScrimController mScrimController;
     // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
     // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
@@ -554,7 +556,9 @@
             QSDetailDisplayer qsDetailDisplayer,
             ScrimController scrimController,
             MediaDataManager mediaDataManager,
-            AmbientState ambientState) {
+            AmbientState ambientState,
+            FeatureFlags featureFlags
+    ) {
         super(view, falsingManager, dozeLog, keyguardStateController,
                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
                 latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
@@ -571,6 +575,7 @@
         mNotificationIconAreaController = notificationIconAreaController;
         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
         mQSDetailDisplayer = qsDetailDisplayer;
+        mFeatureFlags = featureFlags;
         mView.setWillNotDraw(!DEBUG);
         mLayoutInflater = layoutInflater;
         mFalsingManager = falsingManager;
@@ -756,21 +761,38 @@
 
     public void updateResources() {
         int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
-        int panelGravity = mResources.getInteger(R.integer.notification_panel_layout_gravity);
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsFrame.getLayoutParams();
-        if (lp.width != qsWidth || lp.gravity != panelGravity) {
+        ViewGroup.LayoutParams lp = mQsFrame.getLayoutParams();
+        if (lp.width != qsWidth) {
             lp.width = qsWidth;
-            lp.gravity = panelGravity;
             mQsFrame.setLayoutParams(lp);
         }
 
         int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
         lp = mNotificationStackScrollLayoutController.getLayoutParams();
-        if (lp.width != panelWidth || lp.gravity != panelGravity) {
+        if (lp.width != panelWidth) {
             lp.width = panelWidth;
-            lp.gravity = panelGravity;
             mNotificationStackScrollLayoutController.setLayoutParams(lp);
         }
+
+        if (shouldUseSplitNotificationShade()) {
+            // In order to change the constraints at runtime, all children of the Constraint Layout
+            // must have ids.
+            ensureAllViewsHaveIds(mNotificationContainerParent);
+        }
+    }
+
+    private boolean shouldUseSplitNotificationShade() {
+        return mFeatureFlags.isTwoColumnNotificationShadeEnabled()
+                && mResources.getBoolean(R.bool.config_use_split_notification_shade);
+    }
+
+    private static void ensureAllViewsHaveIds(ViewGroup parentView) {
+        for (int i = 0; i < parentView.getChildCount(); i++) {
+            View childView = parentView.getChildAt(i);
+            if (childView.getId() == View.NO_ID) {
+                childView.setId(View.generateViewId());
+            }
+        }
     }
 
     private void reInflateViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 27c94d2..b367406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -28,6 +28,7 @@
 import android.widget.FrameLayout;
 
 import androidx.annotation.DimenRes;
+import androidx.constraintlayout.widget.ConstraintLayout;
 
 import com.android.systemui.R;
 import com.android.systemui.fragments.FragmentHostManager;
@@ -42,7 +43,7 @@
 /**
  * The container with notification stack scroller and quick settings inside.
  */
-public class NotificationsQuickSettingsContainer extends FrameLayout
+public class NotificationsQuickSettingsContainer extends ConstraintLayout
         implements OnInflateListener, FragmentListener,
         AboveShelfObserver.HasViewAboveShelfChangedListener {
 
@@ -68,11 +69,11 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mQsFrame = (FrameLayout) findViewById(R.id.qs_frame);
+        mQsFrame = findViewById(R.id.qs_frame);
         mStackScroller = findViewById(R.id.notification_stack_scroller);
         mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
         mKeyguardStatusBar = findViewById(R.id.keyguard_header);
-        ViewStub userSwitcher = (ViewStub) findViewById(R.id.keyguard_user_switcher);
+        ViewStub userSwitcher = findViewById(R.id.keyguard_user_switcher);
         userSwitcher.setOnInflateListener(this);
         mUserSwitcher = userSwitcher;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index c0a5ffa..31a432e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -44,6 +44,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.util.leak.RotationUtils;
 
+import java.util.List;
 import java.util.Objects;
 
 public class PhoneStatusBarView extends PanelBar {
@@ -75,6 +76,8 @@
     @Nullable
     private DisplayCutout mDisplayCutout;
     private int mStatusBarHeight;
+    @Nullable
+    private List<StatusBar.ExpansionChangedListener> mExpansionChangedListeners;
 
     /**
      * Draw this many pixels into the left/right side of the cutout to optimally use the space
@@ -93,6 +96,11 @@
         mBar = bar;
     }
 
+    public void setExpansionChangedListeners(
+            @Nullable List<StatusBar.ExpansionChangedListener> listeners) {
+        mExpansionChangedListeners = listeners;
+    }
+
     public void setScrimController(ScrimController scrimController) {
         mScrimController = scrimController;
     }
@@ -277,6 +285,12 @@
         if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
             mBar.getNavigationBarView().onStatusBarPanelStateChanged();
         }
+
+        if (mExpansionChangedListeners != null) {
+            for (StatusBar.ExpansionChangedListener listener : mExpansionChangedListeners) {
+                listener.onExpansionChanged(frac, expanded);
+            }
+        }
     }
 
     private void updateScrimFraction() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e39065b..7b2330b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -34,7 +34,6 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -64,8 +63,6 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -201,16 +198,6 @@
     private boolean mWakeLockHeld;
     private boolean mKeyguardOccluded;
 
-    /**
-     * Notifies listeners of animation-related changes (currently just opacity changes).
-     */
-    public interface ScrimChangedListener {
-        void onAlphaChanged(float alpha);
-    }
-
-    @NonNull
-    private final List<ScrimChangedListener> mScrimChangedListeners;
-
     @Inject
     public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters,
             AlarmManager alarmManager, KeyguardStateController keyguardStateController,
@@ -223,7 +210,6 @@
         ScrimState.BUBBLE_EXPANDED.setBubbleAlpha(featureFlags.isShadeOpaque()
                 ? BUSY_SCRIM_ALPHA : GAR_SCRIM_ALPHA);
         mBlurUtils = blurUtils;
-        mScrimChangedListeners = new ArrayList<>();
 
         mKeyguardStateController = keyguardStateController;
         mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
@@ -301,10 +287,6 @@
         mScrimVisibleListener = listener;
     }
 
-    public void addScrimChangedListener(@NonNull ScrimChangedListener listener) {
-        mScrimChangedListeners.add(listener);
-    }
-
     public void transitionTo(ScrimState state) {
         transitionTo(state, null);
     }
@@ -580,10 +562,6 @@
             throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
                     + ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha);
         }
-
-        for (ScrimChangedListener listener : mScrimChangedListeners) {
-            listener.onAlphaChanged(mBehindAlpha);
-        }
     }
 
     private void applyAndDispatchExpansion() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 4a3d8d6..7095afd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -338,6 +338,10 @@
         ONLY_CORE_APPS = onlyCoreApps;
     }
 
+    public interface ExpansionChangedListener {
+        void onExpansionChanged(float expansion, boolean expanded);
+    }
+
     /**
      * The {@link StatusBarState} of the status bar.
      */
@@ -439,6 +443,8 @@
     protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final BrightnessSlider.Factory mBrightnessSliderFactory;
 
+    private final List<ExpansionChangedListener> mExpansionChangedListeners;
+
     // for disabling the status bar
     private int mDisabled1 = 0;
     private int mDisabled2 = 0;
@@ -838,6 +844,8 @@
         mNotificationIconAreaController = notificationIconAreaController;
         mBrightnessSliderFactory = brightnessSliderFactory;
 
+        mExpansionChangedListeners = new ArrayList<>();
+
         mBubbleExpandListener =
                 (isExpanding, key) -> {
                     mContext.getMainExecutor().execute(() -> {
@@ -1077,6 +1085,7 @@
                     mStatusBarView.setBar(this);
                     mStatusBarView.setPanel(mNotificationPanelViewController);
                     mStatusBarView.setScrimController(mScrimController);
+                    mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);
 
                     statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
                     // CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
@@ -4550,4 +4559,8 @@
     public void suppressAmbientDisplay(boolean suppressed) {
         mDozeServiceHost.setDozeSuppressed(suppressed);
     }
+
+    public void addExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
+        mExpansionChangedListeners.add(listener);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
new file mode 100644
index 0000000..1af2c9f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.content.res.Resources
+import android.content.res.TypedArray
+import android.graphics.Canvas
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+import android.util.AttributeSet
+import com.android.systemui.R
+import org.xmlpull.v1.XmlPullParser
+
+/**
+ * [DrawableWrapper] to use in the progress of a slider.
+ *
+ * This drawable is used to change the bounds of the enclosed drawable depending on the level to
+ * simulate a sliding progress, instead of using clipping or scaling. That way, the shape of the
+ * edges is maintained.
+ *
+ * Meant to be used with a rounded ends background, it will also prevent deformation when the slider
+ * is meant to be smaller than the rounded corner. The background should have rounded corners that
+ * are half of the height.
+ */
+class RoundedCornerProgressDrawable(drawable: Drawable?) : DrawableWrapper(drawable) {
+
+    constructor() : this(null)
+
+    companion object {
+        private const val MAX_LEVEL = 10000 // Taken from Drawable
+    }
+
+    private var clipPath: Path = Path()
+
+    init {
+        setClipPath(Rect())
+    }
+
+    override fun inflate(
+        r: Resources,
+        parser: XmlPullParser,
+        attrs: AttributeSet,
+        theme: Resources.Theme?
+    ) {
+        val a = obtainAttributes(r, theme, attrs, R.styleable.RoundedCornerProgressDrawable)
+
+        // Inflation will advance the XmlPullParser and AttributeSet.
+        super.inflate(r, parser, attrs, theme)
+
+        updateStateFromTypedArray(a)
+        if (drawable == null) {
+            throw IllegalStateException("${this::class.java.simpleName} needs a drawable")
+        }
+        a.recycle()
+    }
+
+    override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean {
+        onLevelChange(level)
+        return super.onLayoutDirectionChanged(layoutDirection)
+    }
+
+    private fun updateStateFromTypedArray(a: TypedArray) {
+        if (a.hasValue(R.styleable.RoundedCornerProgressDrawable_android_drawable)) {
+            setDrawable(a.getDrawable(R.styleable.RoundedCornerProgressDrawable_android_drawable))
+        }
+    }
+
+    override fun onBoundsChange(bounds: Rect) {
+        setClipPath(bounds)
+        super.onBoundsChange(bounds)
+        onLevelChange(level)
+    }
+
+    private fun setClipPath(bounds: Rect) {
+        clipPath.reset()
+        clipPath.addRoundRect(
+                bounds.left.toFloat(),
+                bounds.top.toFloat(),
+                bounds.right.toFloat(),
+                bounds.bottom.toFloat(),
+                bounds.height().toFloat() / 2,
+                bounds.height().toFloat() / 2,
+                Path.Direction.CW
+        )
+    }
+
+    override fun onLevelChange(level: Int): Boolean {
+        val db = drawable?.bounds!!
+        val width = bounds.width() * level / MAX_LEVEL
+        // Extra space on the left to keep the rounded shape on the right end
+        val leftBound = bounds.left - bounds.height()
+        drawable?.setBounds(leftBound, db.top, bounds.left + width, db.bottom)
+        return super.onLevelChange(level)
+    }
+
+    override fun draw(canvas: Canvas) {
+        canvas.save()
+        canvas.clipPath(clipPath)
+        super.draw(canvas)
+        canvas.restore()
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 416de04..0795d89 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -29,15 +29,19 @@
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
 import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.pip.tv.TvPipController;
 import com.android.wm.shell.pip.tv.TvPipMenuController;
 import com.android.wm.shell.pip.tv.TvPipNotificationController;
+import com.android.wm.shell.pip.tv.TvPipTransition;
+import com.android.wm.shell.transition.Transitions;
 
 import java.util.Optional;
 
@@ -58,6 +62,7 @@
             PipTaskOrganizer pipTaskOrganizer,
             TvPipMenuController tvPipMenuController,
             PipMediaController pipMediaController,
+            PipTransitionController pipTransitionController,
             TvPipNotificationController tvPipNotificationController,
             TaskStackListenerImpl taskStackListener,
             WindowManagerShellWrapper windowManagerShellWrapper,
@@ -68,6 +73,7 @@
                         pipBoundsState,
                         pipBoundsAlgorithm,
                         pipTaskOrganizer,
+                        pipTransitionController,
                         tvPipMenuController,
                         pipMediaController,
                         tvPipNotificationController,
@@ -92,6 +98,16 @@
     // Handler needed for loadDrawableAsync() in PipControlsViewController
     @WMSingleton
     @Provides
+    static PipTransitionController provideTvPipTransition(
+            Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
+            PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipBoundsState pipBoundsState, TvPipMenuController pipMenuController) {
+        return new TvPipTransition(pipBoundsState, pipMenuController,
+                pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
+    }
+
+    @WMSingleton
+    @Provides
     static TvPipMenuController providesTvPipMenuController(
             Context context,
             PipBoundsState pipBoundsState,
@@ -113,16 +129,26 @@
 
     @WMSingleton
     @Provides
+    static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
+            pipSurfaceTransactionHelper) {
+        return new PipAnimationController(pipSurfaceTransactionHelper);
+    }
+
+    @WMSingleton
+    @Provides
     static PipTaskOrganizer providePipTaskOrganizer(Context context,
             TvPipMenuController tvPipMenuController,
             PipBoundsState pipBoundsState,
             PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipAnimationController pipAnimationController,
+            PipTransitionController pipTransitionController,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController,
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
-                tvPipMenuController, pipSurfaceTransactionHelper, splitScreenOptional,
-                displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+                tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
+                pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
+                shellTaskOrganizer, mainExecutor);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 12a3b5d..2aaa095 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -17,13 +17,11 @@
 package com.android.systemui.wmshell;
 
 import android.animation.AnimationHandler;
-import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.os.Handler;
 import android.view.IWindowManager;
 
 import com.android.systemui.dagger.WMSingleton;
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.apppairs.AppPairs;
@@ -41,18 +39,19 @@
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
 import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
 import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransition;
+import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.pip.phone.PhonePipMenuController;
 import com.android.wm.shell.pip.phone.PipAppOpsListener;
 import com.android.wm.shell.pip.phone.PipController;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
-import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.Optional;
@@ -104,12 +103,13 @@
             PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
             PipBoundsState pipBoundsState, PipMediaController pipMediaController,
             PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
-            PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
+            PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
+            WindowManagerShellWrapper windowManagerShellWrapper,
             TaskStackListenerImpl taskStackListener,
             @ShellMainThread ShellExecutor mainExecutor) {
         return Optional.ofNullable(PipController.create(context, displayController,
                 pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController,
-                phonePipMenuController, pipTaskOrganizer, pipTouchHandler,
+                phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController,
                 windowManagerShellWrapper, taskStackListener, mainExecutor));
     }
 
@@ -143,12 +143,13 @@
             PhonePipMenuController menuPhoneController, PipBoundsAlgorithm pipBoundsAlgorithm,
             PipBoundsState pipBoundsState,
             PipTaskOrganizer pipTaskOrganizer,
+            PipTransitionController pipTransitionController,
             FloatingContentCoordinator floatingContentCoordinator,
             PipUiEventLogger pipUiEventLogger,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTouchHandler(context, menuPhoneController, pipBoundsAlgorithm,
-                pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger,
-                mainExecutor);
+                pipBoundsState, pipTaskOrganizer, pipTransitionController,
+                floatingContentCoordinator, pipUiEventLogger, mainExecutor);
     }
 
     @WMSingleton
@@ -157,12 +158,32 @@
             PipBoundsState pipBoundsState,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PhonePipMenuController menuPhoneController,
+            PipAnimationController pipAnimationController,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+            PipTransitionController pipTransitionController,
             Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController,
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
-                menuPhoneController, pipSurfaceTransactionHelper, splitScreenOptional,
-                displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+                menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
+                pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
+                shellTaskOrganizer, mainExecutor);
+    }
+
+    @WMSingleton
+    @Provides
+    static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
+            pipSurfaceTransactionHelper) {
+        return new PipAnimationController(pipSurfaceTransactionHelper);
+    }
+
+    @WMSingleton
+    @Provides
+    static PipTransitionController providePipTransitionController(Context context,
+            Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
+            PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipBoundsState pipBoundsState, PhonePipMenuController pipMenuController) {
+        return new PipTransition(context, pipBoundsState, pipMenuController,
+                pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 1e473cd..3e873d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -44,7 +44,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -88,7 +88,7 @@
     @Mock
     private StatusBarStateController mStatusBarStateController;
     @Mock
-    private ScrimController mScrimController;
+    private StatusBar mStatusBar;
 
     private FakeExecutor mFgExecutor;
 
@@ -126,7 +126,7 @@
                 mWindowManager,
                 mStatusBarStateController,
                 mFgExecutor,
-                mScrimController);
+                mStatusBar);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
 
@@ -241,6 +241,6 @@
     @Test
     public void registersViewForCallbacks() throws RemoteException {
         verify(mStatusBarStateController).addCallback(mUdfpsView);
-        verify(mScrimController).addScrimChangedListener(mUdfpsView);
+        verify(mStatusBar).addExpansionChangedListener(mUdfpsView);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index b7d1bc6..6db21f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -362,34 +362,6 @@
     }
 
     @Test
-    public void testAugmentTileFromStorageWithNotification() {
-        PeopleSpaceTile tile =
-                new PeopleSpaceTile
-                        .Builder("id", "userName", ICON, new Intent())
-                        .build();
-        PeopleSpaceTile actual = PeopleSpaceUtils
-                .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITH_SHORTCUT);
-
-        assertThat(actual.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
-        assertThat(actual.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
-        assertThat(actual.getNotificationDataUri()).isEqualTo(URI);
-    }
-
-    @Test
-    public void testAugmentTileFromStorageWithoutNotification() {
-        PeopleSpaceTile tile =
-                new PeopleSpaceTile
-                        .Builder("id", "userName", ICON, new Intent())
-                        .build();
-        PeopleSpaceTile actual = PeopleSpaceUtils
-                .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITHOUT_SHORTCUT);
-
-        assertThat(actual.getNotificationKey()).isEqualTo(null);
-        assertThat(actual.getNotificationKey()).isEqualTo(null);
-        assertThat(actual.getNotificationDataUri()).isEqualTo(null);
-    }
-
-    @Test
     public void testDoNotUpdateSingleConversationAppWidgetWhenNotBirthday() {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
         when(mMockCursor.moveToNext()).thenReturn(true, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 8c0afb8..ef314ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -21,6 +21,8 @@
 
 import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -31,26 +33,24 @@
 
 import static java.util.Objects.requireNonNull;
 
-import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.Person;
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
 import android.app.people.PeopleSpaceTile;
 import android.appwidget.AppWidgetManager;
+import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
-import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
-import android.widget.RemoteViews;
 
 import androidx.preference.PreferenceManager;
 import androidx.test.filters.SmallTest;
@@ -58,6 +58,7 @@
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.people.PeopleSpaceUtils;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 import com.android.systemui.statusbar.SbnBuilder;
@@ -74,26 +75,27 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
     private static final long MIN_LINGER_DURATION = 5;
 
-    private static final String TEST_PACKAGE_A = "com.test.package_a";
+    private static final String TEST_PACKAGE_A = "com.android.systemui.tests";
     private static final String TEST_PACKAGE_B = "com.test.package_b";
     private static final String TEST_CHANNEL_ID = "channel_id";
     private static final String TEST_CHANNEL_NAME = "channel_name";
     private static final String TEST_PARENT_CHANNEL_ID = "parent_channel_id";
     private static final String TEST_CONVERSATION_ID = "conversation_id";
     private static final int WIDGET_ID_WITH_SHORTCUT = 1;
+    private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
     private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
     private static final String SHORTCUT_ID = "101";
     private static final String OTHER_SHORTCUT_ID = "102";
-    private static final String NOTIFICATION_KEY = "notification_key";
-    private static final String NOTIFICATION_CONTENT = "notification_content";
+    private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
+    private static final String NOTIFICATION_CONTENT = "message text";
     private static final Uri URI = Uri.parse("fake_uri");
     private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
     private static final Person PERSON = new Person.Builder()
@@ -105,10 +107,14 @@
     private static final PeopleSpaceTile PERSON_TILE =
             new PeopleSpaceTile
                     .Builder(SHORTCUT_ID, "username", ICON, new Intent())
-                    .setNotificationKey(NOTIFICATION_KEY)
+                    .setPackageName(TEST_PACKAGE_A)
+                    .setUid(0)
+                    .setNotificationKey(NOTIFICATION_KEY + "1")
                     .setNotificationContent(NOTIFICATION_CONTENT)
                     .setNotificationDataUri(URI)
                     .build();
+    private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
+            SHORTCUT_ID).setLongLabel("name").build();
 
     private PeopleSpaceWidgetManager mManager;
 
@@ -119,175 +125,101 @@
     @Mock
     private AppWidgetManager mAppWidgetManager;
     @Mock
-    private INotificationManager mINotificationManager;
+    private IPeopleManager mIPeopleManager;
 
     @Captor
     private ArgumentCaptor<NotificationHandler> mListenerCaptor;
+    @Captor
+    private ArgumentCaptor<Bundle> mBundleArgumentCaptor;
 
     private final NoManSimulator mNoMan = new NoManSimulator();
     private final FakeSystemClock mClock = new FakeSystemClock();
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mManager =
                 new PeopleSpaceWidgetManager(mContext);
-        mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mINotificationManager);
+        mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager);
         mManager.attach(mListenerService);
 
         verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
         NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
         mNoMan.addListener(serviceListener);
+        // Default to single People tile widgets.
         Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 2);
+                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
 
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-        SharedPreferences.Editor editor = sp.edit();
-        editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID);
-        editor.apply();
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+
         Bundle options = new Bundle();
-        options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
-
+        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
                 .thenReturn(options);
         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
                 .thenReturn(new Bundle());
+        when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(
+                getConversationWithShortcutId(SHORTCUT_ID));
     }
 
     @Test
-    public void testDoNotNotifyAppWidgetIfNoWidgets() throws RemoteException {
+    public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception {
         int[] widgetIdsArray = {};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        NotifEvent notif1 = mNoMan.postNotif(
-                new NotificationEntryBuilder()
-                        .setId(0)
-                        .setPkg(TEST_PACKAGE_A));
+        StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(sbn)
+                .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testDoNotNotifySingleConversationAppWidgetIfNoWidgets() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
+    public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception {
         int[] widgetIdsArray = {};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        NotifEvent notif1 = mNoMan.postNotif(
-                new NotificationEntryBuilder()
-                        .setId(0)
-                        .setPkg(TEST_PACKAGE_A));
+        Notification notificationWithoutShortcut = new Notification.Builder(mContext)
+                .setContentTitle("TEST_TITLE")
+                .setContentText("TEST_TEXT")
+                .setStyle(new Notification.MessagingStyle(PERSON)
+                        .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+                )
+                .build();
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(new SbnBuilder()
+                        .setNotification(notificationWithoutShortcut)
+                        .setPkg(TEST_PACKAGE_A)
+                        .build())
+                .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any(RemoteViews.class));
-        verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testNotifyAppWidgetIfNotificationPosted() throws RemoteException {
-        int[] widgetIdsArray = {1};
+    public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception {
+        int[] widgetIdsArray = {};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        NotifEvent notif1 = mNoMan.postNotif(
-                new NotificationEntryBuilder()
-                        .setId(0)
-                        .setPkg(TEST_PACKAGE_A));
+        StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
+                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .build();
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(sbnWithoutPackageName)
+                .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, times(1))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mIAppWidgetService, never()).updateAppWidgetIds(any(), any(),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testNotifySingleConversationAppWidgetOnceIfNotificationPosted()
-            throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
-        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-
-        verify(mIAppWidgetService, never())
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
-                any(RemoteViews.class));
-        verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testNotifySingleConversationAppWidgetTwiceIfTwoNotificationsPosted()
-            throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
-        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-        mClock.advanceTime(4);
-        NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_B)
-                .setId(2));
-
-        verify(mIAppWidgetService, never())
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
-                any(RemoteViews.class));
-        verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testNotifyAppWidgetTwiceIfTwoNotificationsPosted() throws RemoteException {
-        int[] widgetIdsArray = {1, 2};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-        mClock.advanceTime(4);
-        NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_B)
-                .setId(2));
-
-        verify(mIAppWidgetService, times(2))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
+                any());
     }
 
     @Test
-    public void testNotifyAppWidgetTwiceIfNotificationPostedAndRemoved() throws RemoteException {
-        int[] widgetIdsArray = {1, 2};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-        mClock.advanceTime(4);
-        NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
-
-        verify(mIAppWidgetService, times(2))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testDoNotNotifyAppWidgetIfNonConversationChannelModified() throws RemoteException {
+    public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception {
         int[] widgetIdsArray = {1};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
@@ -298,13 +230,12 @@
                 UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
+                any());
     }
 
     @Test
-    public void testNotifyAppWidgetIfConversationChannelModified() throws RemoteException {
+    public void testUpdateAppWidgetIfConversationChannelModified() throws Exception {
         int[] widgetIdsArray = {1};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
@@ -316,45 +247,57 @@
                 UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, times(1))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
+        verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testDoNotUpdateNotificationPostedIfNoExistingTile() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+    public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
         verify(mAppWidgetManager, never())
-                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+                .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testDoNotUpdateNotificationRemovedIfNoExistingTile() throws RemoteException {
+    public void testDoNotUpdateNotificationPostedIfDifferentPackageName() throws Exception {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .setPkg(TEST_PACKAGE_B)
+                .build();
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(sbnWithDifferentPackageName)
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, never())
+                .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
+    }
+
+    @Test
+    public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(4);
         NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
@@ -362,99 +305,215 @@
 
         verify(mAppWidgetManager, never())
                 .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testUpdateNotificationPostedIfExistingTile() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+    public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
+        StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .setPkg(TEST_PACKAGE_B)
+                .build();
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
+                .setSbn(sbnWithDifferentPackageName)
+                .setId(1));
+        mClock.advanceTime(4);
+        NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, never())
+                .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
+    }
+
+    @Test
+    public void testUpdateNotificationPostedIfExistingTile() throws Exception {
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createConversationNotification(SHORTCUT_ID))
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
         verify(mAppWidgetManager, times(1))
-                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundle = mBundleArgumentCaptor.getValue();
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
+    public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception {
+        Bundle options = new Bundle();
+        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+        when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+                .thenReturn(options);
+        // Set the same Person associated on another People Tile widget ID.
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+                SECOND_WIDGET_ID_WITH_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createConversationNotification(SHORTCUT_ID))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
+    public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()
+            throws Exception {
+        Bundle options = new Bundle();
+        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+        when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+                .thenReturn(options);
+        // Set the same Person associated on another People Tile widget ID.
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+                SECOND_WIDGET_ID_WITH_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createConversationNotification(SHORTCUT_ID))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        verify(mAppWidgetManager, never())
+                .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, never()).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                any());
     }
 
     @Test
     public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile()
-            throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+            throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        Notification notification = new Notification.Builder(mContext)
+        Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
                 .setContentTitle("TEST_TITLE")
                 .setContentText("TEST_TEXT")
                 .setShortcutId(SHORTCUT_ID)
                 .build();
         StatusBarNotification sbn = new SbnBuilder()
-                .setNotification(notification)
+                .setNotification(notificationWithoutMessagingStyle)
                 .build();
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
         verify(mAppWidgetManager, never())
                 .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
     }
 
     @Test
-    public void testUpdateNotificationRemovedIfExistingTile() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+    public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
         NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mAppWidgetManager, times(2))
-                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+        verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                mBundleArgumentCaptor.capture());
+        Bundle bundle = mBundleArgumentCaptor.getValue();
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        assertThat(tile.getNotificationKey()).isEqualTo(null);
+        assertThat(tile.getNotificationContent()).isEqualTo(null);
+        assertThat(tile.getNotificationDataUri()).isEqualTo(null);
+        verify(mAppWidgetManager, times(2)).updateAppWidget(anyInt(),
+                any());
     }
 
-    /** Returns a list of a single conversation associated with {@code SHORTCUT_ID}. */
-    private List<ConversationChannelWrapper> getConversationWithShortcutId() {
-        List<ConversationChannelWrapper> convos = new ArrayList<>();
-        ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
-        convo1.setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID).setLongLabel(
-                "name").build());
-        convos.add(convo1);
-        return convos;
+    /**
+     * Returns a single conversation associated with {@code shortcutId}.
+     */
+    private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception {
+        ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+                "name").build();
+        ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
+                0L, false);
+        return convo;
     }
 
-    private StatusBarNotification createConversationNotification(String shortcutId) {
-        Notification notification = new Notification.Builder(mContext)
+    private Notification createMessagingStyleNotification(String shortcutId) {
+        return new Notification.Builder(mContext)
                 .setContentTitle("TEST_TITLE")
                 .setContentText("TEST_TEXT")
                 .setShortcutId(shortcutId)
                 .setStyle(new Notification.MessagingStyle(PERSON)
-                        .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+                        .addMessage(
+                                new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+                                        PERSON))
                 )
                 .build();
+    }
+
+    private StatusBarNotification createConversationNotification(String shortcutId) {
+        Notification notification = createMessagingStyleNotification(shortcutId);
         return new SbnBuilder()
                 .setNotification(notification)
+                .setPkg(TEST_PACKAGE_A)
                 .build();
     }
+
+    private void setStorageForTile(String shortcutId, String packageName, int widgetId) {
+        SharedPreferences widgetSp = mContext.getSharedPreferences(
+                String.valueOf(widgetId),
+                Context.MODE_PRIVATE);
+        SharedPreferences.Editor widgetEditor = widgetSp.edit();
+        widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, packageName);
+        widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, shortcutId);
+        widgetEditor.putInt(PeopleSpaceUtils.USER_ID, 0);
+        widgetEditor.apply();
+
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putString(String.valueOf(widgetId), shortcutId);
+        String key = PeopleSpaceUtils.getKey(shortcutId, packageName, 0);
+        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+        storedWidgetIds.add(String.valueOf(widgetId));
+        editor.putStringSet(key, storedWidgetIds);
+        editor.apply();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index c6f97fa..4381158 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -118,7 +118,7 @@
                 mQSTileHost, mQSCustomizerController, true, mMediaHost,
                 mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
                 mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
-                /* labelsFlag */ false, /* sideLabels */ false);
+                /* labelsFlag */ false);
 
         mController.init();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index c490c4c..107160f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -83,7 +83,8 @@
                 metricsLogger,
                 uiEventLogger,
                 qsLogger,
-                dumpManager
+                dumpManager,
+                false
         )
 
         controller.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
index b63274b..451c78f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
@@ -71,7 +72,8 @@
     @Mock private NavigationModeController mMockNavModeController;
     @Mock private NotificationShadeWindowController mMockStatusBarWinController;
     @Mock private Optional<Pip> mMockPipOptional;
-    @Mock private Optional<LegacySplitScreen> mMockSplitScreenOptional;
+    @Mock private Optional<LegacySplitScreen> mMockLegacySplitScreenOptional;
+    @Mock private Optional<SplitScreen> mMockSplitScreenOptional;
     @Mock private Optional<Lazy<StatusBar>> mMockStatusBarOptionalLazy;
     @Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional;
     @Mock private PackageManager mPackageManager;
@@ -89,8 +91,8 @@
 
         mSpiedOverviewProxyService = spy(new OverviewProxyService(mSpiedContext, mMockCommandQueue,
                 mMockNavBarControllerLazy, mMockNavModeController, mMockStatusBarWinController,
-                mMockSysUiState, mMockPipOptional, mMockSplitScreenOptional,
-                mMockStatusBarOptionalLazy, mMockOneHandedOptional,
+                mMockSysUiState, mMockPipOptional, mMockLegacySplitScreenOptional,
+                mMockSplitScreenOptional, mMockStatusBarOptionalLazy, mMockOneHandedOptional,
                 mMockBroadcastDispatcher, mMockTransitions));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 8d4470b..fa78ca6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -68,6 +68,7 @@
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.qs.QSDetailDisplayer;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShelfController;
@@ -204,6 +205,10 @@
     @Mock
     private MediaDataManager mMediaDataManager;
     @Mock
+    private FeatureFlags mFeatureFlags;
+    @Mock
+    private NotificationsQuickSettingsContainer mNotificationContainerParent;
+    @Mock
     private AmbientState mAmbientState;
     private NotificationPanelViewController mNotificationPanelViewController;
     private View.AccessibilityDelegate mAccessibiltyDelegate;
@@ -219,6 +224,8 @@
         when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
         mDisplayMetrics.density = 100;
         when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true);
+        when(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)).thenReturn(400);
+        when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400);
         when(mView.getContext()).thenReturn(getContext());
         when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
         when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
@@ -237,6 +244,8 @@
         when(mView.findViewById(R.id.keyguard_status_view))
                 .thenReturn(mock(KeyguardStatusView.class));
         when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
+        when(mView.findViewById(R.id.notification_container_parent))
+                .thenReturn(mNotificationContainerParent);
         FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
                 mDisplayMetrics);
 
@@ -264,6 +273,11 @@
                 .thenReturn(mKeyguardClockSwitchController);
         when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
                 .thenReturn(mKeyguardStatusViewController);
+        when(mQsFrame.getLayoutParams()).thenReturn(
+                new ViewGroup.LayoutParams(600, 400));
+        when(mNotificationStackScrollLayoutController.getLayoutParams()).thenReturn(
+                new ViewGroup.LayoutParams(600, 400));
+
         mNotificationPanelViewController = new NotificationPanelViewController(mView,
                 mResources,
                 mLayoutInflater,
@@ -285,7 +299,8 @@
                 new QSDetailDisplayer(),
                 mScrimController,
                 mMediaDataManager,
-                mAmbientState);
+                mAmbientState,
+                mFeatureFlags);
         mNotificationPanelViewController.initDependencies(
                 mStatusBar,
                 mNotificationShelfController);
@@ -400,6 +415,25 @@
         verify(mStatusBarKeyguardViewManager).showBouncer(true);
     }
 
+    @Test
+    public void testAllChildrenOfNotificationContainer_haveIds() {
+        when(mNotificationContainerParent.getChildCount()).thenReturn(2);
+        when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+
+        View view1 = new View(mContext);
+        view1.setId(1);
+        when(mNotificationContainerParent.getChildAt(0)).thenReturn(view1);
+
+        View view2 = mock(View.class);
+        when(mNotificationContainerParent.getChildAt(1)).thenReturn(view2);
+
+        mNotificationPanelViewController.updateResources();
+
+        assertThat(mNotificationContainerParent.getChildAt(0).getId()).isEqualTo(1);
+        assertThat(mNotificationContainerParent.getChildAt(1).getId()).isNotEqualTo(View.NO_ID);
+    }
+
     private void onTouchEvent(MotionEvent ev) {
         mTouchHandler.onTouch(mView, ev);
     }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index f9f064c..e4a86c3 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -55,7 +55,6 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
@@ -66,9 +65,8 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
 import android.graphics.Point;
-import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -119,7 +117,6 @@
 import com.android.internal.widget.IRemoteViewsFactory;
 import com.android.server.LocalServices;
 import com.android.server.WidgetBackupProvider;
-import com.android.server.policy.IconUtilities;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -253,8 +250,6 @@
     private boolean mSafeMode;
     private int mMaxWidgetBitmapMemory;
 
-    private IconUtilities mIconUtilities;
-
     AppWidgetServiceImpl(Context context) {
         mContext = context;
     }
@@ -271,7 +266,6 @@
         mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
         mBackupRestoreController = new BackupRestoreController();
         mSecurityPolicy = new SecurityPolicy();
-        mIconUtilities = new IconUtilities(mContext);
 
         computeMaximumWidgetBitmapMemory();
         registerBroadcastReceiver();
@@ -578,44 +572,6 @@
         }
     }
 
-    private Bitmap createMaskedWidgetBitmap(String providerPackage, int providerUserId) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            // Load the unbadged application icon and pass it to the widget to appear on
-            // the masked view.
-            Context userContext = mContext.createPackageContextAsUser(providerPackage, 0,
-                    UserHandle.of(providerUserId));
-            PackageManager pm = userContext.getPackageManager();
-            Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm).mutate();
-            // Create a bitmap of the icon which is what the widget's remoteview requires.
-            icon.setColorFilter(mIconUtilities.getDisabledColorFilter());
-            return mIconUtilities.createIconBitmap(icon);
-        } catch (NameNotFoundException e) {
-            Slog.e(TAG, "Fail to get application icon", e);
-            // Provider package removed, no need to mask its views as its state will be
-            // purged very soon.
-            return null;
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    private RemoteViews createMaskedWidgetRemoteViews(Bitmap icon, boolean showBadge,
-            PendingIntent onClickIntent) {
-        RemoteViews views = new RemoteViews(mContext.getPackageName(),
-                R.layout.work_widget_mask_view);
-        if (icon != null) {
-            views.setImageViewBitmap(R.id.work_widget_app_icon, icon);
-        }
-        if (!showBadge) {
-            views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
-        }
-        if (onClickIntent != null) {
-            views.setOnClickPendingIntent(R.id.work_widget_mask_frame, onClickIntent);
-        }
-        return views;
-    }
-
     /**
      * Mask the target widget belonging to the specified provider, or all active widgets
      * of the provider if target widget == null.
@@ -625,59 +581,63 @@
         if (widgetCount == 0) {
             return;
         }
-        final String providerPackage = provider.id.componentName.getPackageName();
-        final int providerUserId = provider.getUserId();
-        Bitmap iconBitmap = createMaskedWidgetBitmap(providerPackage, providerUserId);
-        if (iconBitmap == null) {
-            return;
-        }
-        final boolean showBadge;
-        final Intent onClickIntent;
+        RemoteViews views = new RemoteViews(mContext.getPackageName(),
+                R.layout.work_widget_mask_view);
+        ApplicationInfo appInfo = provider.info.providerInfo.applicationInfo;
+        final int appUserId = provider.getUserId();
+        boolean showBadge;
+
         final long identity = Binder.clearCallingIdentity();
         try {
+            final Intent onClickIntent;
+
             if (provider.maskedBySuspendedPackage) {
-                showBadge = mUserManager.hasBadge(providerUserId);
+                showBadge = mUserManager.hasBadge(appUserId);
                 final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage(
-                        providerPackage, providerUserId);
+                        appInfo.packageName, appUserId);
                 if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
                     onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
-                            providerUserId, true);
+                            appUserId, true);
                 } else {
                     final SuspendDialogInfo dialogInfo =
-                            mPackageManagerInternal.getSuspendedDialogInfo(providerPackage,
-                                    suspendingPackage, providerUserId);
+                            mPackageManagerInternal.getSuspendedDialogInfo(
+                                    appInfo.packageName, suspendingPackage, appUserId);
                     // onUnsuspend is null because we don't want to start any activity on
                     // unsuspending from a suspended widget.
                     onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
-                            providerPackage, suspendingPackage, dialogInfo, null, null,
-                            providerUserId);
+                            appInfo.packageName, suspendingPackage, dialogInfo, null, null,
+                            appUserId);
                 }
             } else if (provider.maskedByQuietProfile) {
                 showBadge = true;
-                onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
-                        providerUserId);
+                onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(appUserId);
             } else /* provider.maskedByLockedProfile */ {
                 showBadge = true;
-                onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
-                        providerUserId);
+                onClickIntent = mKeyguardManager
+                        .createConfirmDeviceCredentialIntent(null, null, appUserId);
                 if (onClickIntent != null) {
-                    onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK
-                            | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                    onClickIntent.setFlags(
+                            FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                 }
             }
+
+            if (onClickIntent != null) {
+                views.setOnClickPendingIntent(R.id.work_widget_mask_frame,
+                        PendingIntent.getActivity(mContext, 0, onClickIntent,
+                                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
+            }
+
+            Icon icon = appInfo.icon != 0
+                    ? Icon.createWithResource(appInfo.packageName, appInfo.icon)
+                    : Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+            views.setImageViewIcon(R.id.work_widget_app_icon, icon);
+            if (!showBadge) {
+                views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
+            }
+
             for (int j = 0; j < widgetCount; j++) {
                 Widget widget = provider.widgets.get(j);
                 if (targetWidget != null && targetWidget != widget) continue;
-                PendingIntent intent = null;
-                if (onClickIntent != null) {
-                    // Rare informational activity click is okay being
-                    // immutable; the tradeoff is more security in exchange for
-                    // losing bounds-based window animations
-                    intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
-                            onClickIntent,
-                            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-                }
-                RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
                 if (widget.replaceWithMaskedViewsLocked(views)) {
                     scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
                 }
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index 3c5268c..ba2a63a 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -3,6 +3,7 @@
 aabhinav@google.com
 bryanmawhinney@google.com
 jstemmer@google.com
+millmore@google.com
 nathch@google.com
 niagra@google.com
 niamhfw@google.com
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 04e08ae..55e3ef2 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -926,7 +926,7 @@
         mPermissionControllerManager.getPrivilegesDescriptionStringForProfile(
                 deviceProfile, FgThread.getExecutor(), desc -> {
                         try {
-                            result.complete(requireNonNull(desc));
+                            result.complete(desc);
                         } catch (Exception e) {
                             result.completeExceptionally(e);
                         }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6de227e..e01c4df 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -158,9 +158,9 @@
     srcs: [":services.core.unboosted"],
     tools: ["lockedregioncodeinjection"],
     cmd: "$(location lockedregioncodeinjection) " +
-        "  --targets \"Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/wm/WindowManagerGlobalLock;\" " +
-        "  --pre \"com/android/server/am/ActivityManagerService.boostPriorityForLockedSection,com/android/server/wm/WindowManagerService.boostPriorityForLockedSection\" " +
-        "  --post \"com/android/server/am/ActivityManagerService.resetPriorityAfterLockedSection,com/android/server/wm/WindowManagerService.resetPriorityAfterLockedSection\" " +
+        "  --targets \"Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/am/ActivityManagerGlobalLock;,Lcom/android/server/wm/WindowManagerGlobalLock;\" " +
+        "  --pre \"com/android/server/am/ActivityManagerService.boostPriorityForLockedSection,com/android/server/am/ActivityManagerService.boostPriorityForProcLockedSection,com/android/server/wm/WindowManagerService.boostPriorityForLockedSection\" " +
+        "  --post \"com/android/server/am/ActivityManagerService.resetPriorityAfterLockedSection,com/android/server/am/ActivityManagerService.resetPriorityAfterProcLockedSection,com/android/server/wm/WindowManagerService.resetPriorityAfterLockedSection\" " +
         "  -o $(out) " +
         "  -i $(in)",
     out: ["services.core.priorityboosted.jar"],
diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS
new file mode 100644
index 0000000..5eed0b5
--- /dev/null
+++ b/services/core/java/android/content/pm/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/pm/OWNERS
\ No newline at end of file
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index dad8bd8..9ccb0c7 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1137,7 +1137,8 @@
      */
     public abstract void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int optional, @Checksum.Type int required,
-            @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+            @Nullable List trustedInstallers,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
             @NonNull Executor executor, @NonNull Handler handler);
 
     /**
diff --git a/services/core/java/android/os/OWNERS b/services/core/java/android/os/OWNERS
new file mode 100644
index 0000000..d0a2daf
--- /dev/null
+++ b/services/core/java/android/os/OWNERS
@@ -0,0 +1 @@
+per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
diff --git a/services/core/java/android/power/PowerStatsInternal.java b/services/core/java/android/power/PowerStatsInternal.java
index f7417da..40107a5 100644
--- a/services/core/java/android/power/PowerStatsInternal.java
+++ b/services/core/java/android/power/PowerStatsInternal.java
@@ -18,8 +18,12 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.power.stats.Channel;
 import android.hardware.power.stats.EnergyConsumer;
 import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.StateResidencyResult;
 
 import java.util.concurrent.CompletableFuture;
 
@@ -51,4 +55,45 @@
     @NonNull
     public abstract CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
             int[] energyConsumerIds);
+
+    /**
+     * Returns the power entity info for all available {@link PowerEntity}
+     *
+     * @return List of available {@link PowerEntity}
+     */
+    public abstract PowerEntity[] getPowerEntityInfo();
+
+    /**
+     * Returns a CompletableFuture that will get a {@link StateResidencyResult} array for the
+     * available requested power entities.
+     *
+     * @param powerEntityIds Array of {@link PowerEntity.id} for which state residency is being
+     *                          requested.
+     *
+     * @return A Future containing a list of {@link StateResidencyResult} objects containing state
+     *         residency results for all listed {@link PowerEntity.id}.
+     */
+    @NonNull
+    public abstract CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
+            int[] powerEntityIds);
+
+    /**
+     * Returns the channel info for all available {@link Channel}
+     *
+     * @return List of available {@link Channel}
+     */
+    public abstract Channel[] getEnergyMeterInfo();
+
+    /**
+     * Returns a CompletableFuture that will get a {@link EnergyMeasurement} array for the
+     * available requested channels.
+     *
+     * @param channelIds Array of {@link Channel.id} for accumulated energy is being requested.
+     *
+     * @return A Future containing a list of {@link EnergyMeasurement} objects containing
+     *         accumulated energy measurements for all listed {@link Channel.id}.
+     */
+    @NonNull
+    public abstract CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
+            int[] channelIds);
 }
diff --git a/services/core/java/com/android/server/BundleUtils.java b/services/core/java/com/android/server/BundleUtils.java
new file mode 100644
index 0000000..20ebe29
--- /dev/null
+++ b/services/core/java/com/android/server/BundleUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+
+/**
+ * Utility methods for handling {@link Bundle}.
+ *
+ */
+public final class BundleUtils {
+    private BundleUtils() {
+    }
+
+    /**
+     * Returns true if {@code in} is null or empty.
+     */
+    public static boolean isEmpty(@Nullable Bundle in) {
+        return (in == null) || (in.size() == 0);
+    }
+
+    /**
+     * Creates a copy of the {@code in} Bundle.  If {@code in} is null, it'll return an empty
+     * bundle.
+     *
+     * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
+     * {@link Bundle#EMPTY})
+     */
+    public static @NonNull Bundle clone(@Nullable Bundle in) {
+        return (in != null) ? new Bundle(in) : new Bundle();
+    }
+
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f2e1920..bf9d564 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -222,6 +222,7 @@
 import java.io.PrintWriter;
 import java.net.Inet4Address;
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -739,11 +740,11 @@
         }
 
         private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
-                boolean isFallbackNetwork) {
+                boolean isDefaultNetwork) {
             if (DBG) {
                 log("Sending " + state
                         + " broadcast for type " + type + " " + nai.toShortString()
-                        + " isFallbackNetwork=" + isFallbackNetwork);
+                        + " isDefaultNetwork=" + isDefaultNetwork);
             }
         }
 
@@ -762,10 +763,10 @@
                 list.add(nai);
             }
 
-            // Send a broadcast if this is the first network of its type or if it's the fallback.
-            final boolean isFallbackNetwork = mService.isFallbackNetwork(nai);
-            if ((list.size() == 1) || isFallbackNetwork) {
-                maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isFallbackNetwork);
+            // Send a broadcast if this is the first network of its type or if it's the default.
+            final boolean isDefaultNetwork = mService.isDefaultNetwork(nai);
+            if ((list.size() == 1) || isDefaultNetwork) {
+                maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork);
                 mService.sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
             }
         }
@@ -794,7 +795,7 @@
                               ", sending connected broadcast");
                 final NetworkAgentInfo replacement = list.get(0);
                 maybeLogBroadcast(replacement, DetailedState.CONNECTED, type,
-                        mService.isFallbackNetwork(replacement));
+                        mService.isDefaultNetwork(replacement));
                 mService.sendLegacyNetworkBroadcast(replacement, DetailedState.CONNECTED, type);
             }
         }
@@ -810,14 +811,14 @@
         // send out another legacy broadcast - currently only used for suspend/unsuspend
         // toggle
         public void update(NetworkAgentInfo nai) {
-            final boolean isFallback = mService.isFallbackNetwork(nai);
+            final boolean isDefault = mService.isDefaultNetwork(nai);
             final DetailedState state = nai.networkInfo.getDetailedState();
             for (int type = 0; type < mTypeLists.length; type++) {
                 final ArrayList<NetworkAgentInfo> list = mTypeLists[type];
                 final boolean contains = (list != null && list.contains(nai));
                 final boolean isFirst = contains && (nai == list.get(0));
-                if (isFirst || contains && isFallback) {
-                    maybeLogBroadcast(nai, state, type, isFallback);
+                if (isFirst || contains && isDefault) {
+                    maybeLogBroadcast(nai, state, type, isDefault);
                     mService.sendLegacyNetworkBroadcast(nai, state, type);
                 }
             }
@@ -990,6 +991,15 @@
         }
 
         /**
+         * Gets the UID that owns a socket connection. Needed because opening SOCK_DIAG sockets
+         * requires CAP_NET_ADMIN, which the unit tests do not have.
+         */
+        public int getConnectionOwnerUid(int protocol, InetSocketAddress local,
+                InetSocketAddress remote) {
+            return InetDiagMessage.getConnectionOwnerUid(protocol, local, remote);
+        }
+
+        /**
          * @see MultinetworkPolicyTracker
          */
         public MultinetworkPolicyTracker makeMultinetworkPolicyTracker(
@@ -1022,12 +1032,12 @@
 
         mMetricsLog = logger;
         mNetworkRanker = new NetworkRanker();
-        final NetworkRequest fallbackRequest = createDefaultInternetRequestForTransport(
+        final NetworkRequest defaultInternetRequest = createDefaultInternetRequestForTransport(
                 -1, NetworkRequest.Type.REQUEST);
-        mFallbackRequest = new NetworkRequestInfo(null, fallbackRequest, new Binder());
-        mNetworkRequests.put(fallbackRequest, mFallbackRequest);
-        mDefaultNetworkRequests.add(mFallbackRequest);
-        mNetworkRequestInfoLogs.log("REGISTER " + mFallbackRequest);
+        mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder());
+        mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
+        mDefaultNetworkRequests.add(mDefaultRequest);
+        mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest);
 
         mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
                 NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
@@ -1217,6 +1227,14 @@
 
         mDnsManager = new DnsManager(mContext, mDnsResolver);
         registerPrivateDnsSettingsCallbacks();
+
+        mNoServiceNetwork =  new NetworkAgentInfo(null,
+                new Network(NO_SERVICE_NET_ID),
+                new NetworkInfo(TYPE_NONE, 0, "", ""),
+                new LinkProperties(), new NetworkCapabilities(), 0, mContext,
+                null, new NetworkAgentConfig(), this, null,
+                null, null, 0, INVALID_UID,
+                mQosCallbackTracker);
     }
 
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1366,7 +1384,7 @@
     }
 
     private NetworkState getUnfilteredActiveNetworkState(int uid) {
-        NetworkAgentInfo nai = getFallbackNetwork();
+        NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
 
         final Network[] networks = getVpnUnderlyingNetworks(uid);
         if (networks != null) {
@@ -1499,7 +1517,7 @@
             }
         }
 
-        NetworkAgentInfo nai = getFallbackNetwork();
+        NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
         if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid,
                 ignoreBlocked)) {
             return null;
@@ -1638,21 +1656,28 @@
 
         HashMap<Network, NetworkCapabilities> result = new HashMap<>();
 
-        final NetworkAgentInfo nai = getFallbackNetwork();
-        NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
-        if (nc != null) {
-            result.put(
-                    nai.network,
-                    createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                            nc, mDeps.getCallingUid(), callingPackageName));
+        for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+            if (!nri.isBeingSatisfied()) {
+                continue;
+            }
+            final NetworkAgentInfo nai = nri.getSatisfier();
+            final NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
+            if (null != nc
+                    && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+                    && !result.containsKey(nai.network)) {
+                result.put(
+                        nai.network,
+                        createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+                                nc, mDeps.getCallingUid(), callingPackageName));
+            }
         }
 
         // No need to check mLockdownEnabled. If it's true, getVpnUnderlyingNetworks returns null.
         final Network[] networks = getVpnUnderlyingNetworks(Binder.getCallingUid());
-        if (networks != null) {
-            for (Network network : networks) {
-                nc = getNetworkCapabilitiesInternal(network);
-                if (nc != null) {
+        if (null != networks) {
+            for (final Network network : networks) {
+                final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
+                if (null != nc) {
                     result.put(
                             network,
                             createWithLocationInfoSanitizedIfNecessaryWhenParceled(
@@ -1674,9 +1699,7 @@
 
     /**
      * Return LinkProperties for the active (i.e., connected) default
-     * network interface.  It is assumed that at most one default network
-     * is active at a time. If more than one is active, it is indeterminate
-     * which will be returned.
+     * network interface for the calling uid.
      * @return the ip properties for the active network, or {@code null} if
      * none is active
      */
@@ -2025,7 +2048,7 @@
             // TODO: Move the Dns Event to NetworkMonitor. NetdEventListenerService only allow one
             // callback from each caller type. Need to re-factor NetdEventListenerService to allow
             // multiple NetworkMonitor registrants.
-            if (nai != null && nai.satisfies(mFallbackRequest.mRequests.get(0))) {
+            if (nai != null && nai.satisfies(mDefaultRequest.mRequests.get(0))) {
                 nai.networkMonitor().notifyDnsResponse(returnCode);
             }
         }
@@ -2119,8 +2142,8 @@
 
     private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered,
             boolean isBackgroundRestricted) {
-        return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
-                isNetworkMetered, isBackgroundRestricted);
+        return mPolicyManager.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+                isBackgroundRestricted);
     }
 
     /**
@@ -2582,12 +2605,12 @@
         pw.println();
         pw.println();
 
-        final NetworkAgentInfo fallbackNai = getFallbackNetwork();
+        final NetworkAgentInfo defaultNai = getDefaultNetwork();
         pw.print("Active default network: ");
-        if (fallbackNai == null) {
+        if (defaultNai == null) {
             pw.println("none");
         } else {
-            pw.println(fallbackNai.network.getNetId());
+            pw.println(defaultNai.network.getNetId());
         }
         pw.println();
 
@@ -2703,9 +2726,9 @@
                 pw.println(nai.requestAt(i).toString());
             }
             pw.decreaseIndent();
-            pw.println("Lingered:");
+            pw.println("Inactivity Timers:");
             pw.increaseIndent();
-            nai.dumpLingerTimers(pw);
+            nai.dumpInactivityTimers(pw);
             pw.decreaseIndent();
             pw.decreaseIndent();
         }
@@ -2970,7 +2993,7 @@
 
             final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
             final boolean wasValidated = nai.lastValidated;
-            final boolean wasFallback = isFallbackNetwork(nai);
+            final boolean wasDefault = isDefaultNetwork(nai);
 
             if (DBG) {
                 final String logMsg = !TextUtils.isEmpty(redirectUrl)
@@ -2979,7 +3002,7 @@
                 log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
             }
             if (valid != nai.lastValidated) {
-                if (wasFallback) {
+                if (wasDefault) {
                     mMetricsLog.logDefaultNetworkValidity(valid);
                 }
                 final int oldScore = nai.getCurrentScore();
@@ -3300,27 +3323,27 @@
     }
 
     /**
-     * Updates the linger state from the network requests inside the NAI.
+     * Updates the inactivity state from the network requests inside the NAI.
      * @param nai the agent info to update
      * @param now the timestamp of the event causing this update
-     * @return whether the network was lingered as a result of this update
+     * @return whether the network was inactive as a result of this update
      */
-    private boolean updateLingerState(@NonNull final NetworkAgentInfo nai, final long now) {
-        // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
-        // 2. If the network was lingering and there are now requests, unlinger it.
+    private boolean updateInactivityState(@NonNull final NetworkAgentInfo nai, final long now) {
+        // 1. Update the inactivity timer. If it's changed, reschedule or cancel the alarm.
+        // 2. If the network was inactive and there are now requests, unset inactive.
         // 3. If this network is unneeded (which implies it is not lingering), and there is at least
-        //    one lingered request, start lingering.
-        nai.updateLingerTimer();
+        //    one lingered request, set inactive.
+        nai.updateInactivityTimer();
         if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
-            if (DBG) log("Unlingering " + nai.toShortString());
-            nai.unlinger();
+            if (DBG) log("Unsetting inactive " + nai.toShortString());
+            nai.unsetInactive();
             logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
-        } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
+        } else if (unneeded(nai, UnneededFor.LINGER) && nai.getInactivityExpiry() > 0) {
             if (DBG) {
-                final int lingerTime = (int) (nai.getLingerExpiry() - now);
-                log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
+                final int lingerTime = (int) (nai.getInactivityExpiry() - now);
+                log("Setting inactive " + nai.toShortString() + " for " + lingerTime + "ms");
             }
-            nai.linger();
+            nai.setInactive();
             logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
             return true;
         }
@@ -3334,7 +3357,6 @@
                 if (VDBG) log("NetworkFactory connected");
                 // Finish setting up the full connection
                 NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
-                npi.completeConnection();
                 sendAllRequestsToProvider(npi);
             } else {
                 loge("Error connecting NetworkFactory");
@@ -3355,13 +3377,13 @@
             loge("Error connecting NetworkAgent");
             mNetworkAgentInfos.remove(nai);
             if (nai != null) {
-                final boolean wasFallback = isFallbackNetwork(nai);
+                final boolean wasDefault = isDefaultNetwork(nai);
                 synchronized (mNetworkForNetId) {
                     mNetworkForNetId.remove(nai.network.getNetId());
                 }
                 mNetIdManager.releaseNetId(nai.network.getNetId());
                 // Just in case.
-                mLegacyTypeTracker.remove(nai, wasFallback);
+                mLegacyTypeTracker.remove(nai, wasDefault);
             }
         }
     }
@@ -3400,8 +3422,8 @@
             nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
                     null, null);
         }
-        final boolean wasFallback = isFallbackNetwork(nai);
-        if (wasFallback) {
+        final boolean wasDefault = isDefaultNetwork(nai);
+        if (wasDefault) {
             mDefaultInetConditionPublished = 0;
             // Log default network disconnection before required book-keeping.
             // Let rematchAllNetworksAndRequests() below record a new default network event
@@ -3436,17 +3458,20 @@
         propagateUnderlyingNetworkCapabilities(nai.network);
         // Remove all previously satisfied requests.
         for (int i = 0; i < nai.numNetworkRequests(); i++) {
-            NetworkRequest request = nai.requestAt(i);
+            final NetworkRequest request = nai.requestAt(i);
             final NetworkRequestInfo nri = mNetworkRequests.get(request);
             final NetworkAgentInfo currentNetwork = nri.getSatisfier();
             if (currentNetwork != null
                     && currentNetwork.network.getNetId() == nai.network.getNetId()) {
+                // uid rules for this network will be removed in destroyNativeNetwork(nai).
                 nri.setSatisfier(null, null);
-                sendUpdatedScoreToFactories(request, null);
+                if (request.isRequest()) {
+                    sendUpdatedScoreToFactories(request, null);
+                }
 
-                if (mFallbackRequest == nri) {
+                if (mDefaultRequest == nri) {
                     // TODO : make battery stats aware that since 2013 multiple interfaces may be
-                    //  active at the same time. For now keep calling this with the fallback
+                    //  active at the same time. For now keep calling this with the default
                     //  network, because while incorrect this is the closest to the old (also
                     //  incorrect) behavior.
                     mNetworkActivityTracker.updateDataActivityTracking(
@@ -3456,11 +3481,11 @@
                 }
             }
         }
-        nai.clearLingerState();
+        nai.clearInactivityState();
         // TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after.
-        //  Currently, deleting it breaks tests that check for the fallback network disconnecting.
+        //  Currently, deleting it breaks tests that check for the default network disconnecting.
         //  Find out why, fix the rematch code, and delete this.
-        mLegacyTypeTracker.remove(nai, wasFallback);
+        mLegacyTypeTracker.remove(nai, wasDefault);
         rematchAllNetworksAndRequests();
         mLingerMonitor.noteDisconnect(nai);
         if (nai.created) {
@@ -3468,10 +3493,9 @@
             // (routing rules, DNS, etc).
             // This may be slow as it requires a lot of netd shelling out to ip and
             // ip[6]tables to flush routes and remove the incoming packet mark rule, so do it
-            // after we've rematched networks with requests which should make a potential
-            // fallback network the default or requested a new network from the
-            // NetworkProviders, so network traffic isn't interrupted for an unnecessarily
-            // long time.
+            // after we've rematched networks with requests (which might change the default
+            // network or service a new request from an app), so network traffic isn't interrupted
+            // for an unnecessarily long time.
             destroyNativeNetwork(nai);
             mDnsManager.removeNetwork(nai.network);
         }
@@ -3556,8 +3580,8 @@
             }
         }
         rematchAllNetworksAndRequests();
-        // If an active request exists, return as its score has already been sent if needed.
-        if (null != nri.getActiveRequest()) {
+        // If the nri is satisfied, return as its score has already been sent if needed.
+        if (nri.isBeingSatisfied()) {
             return;
         }
 
@@ -3700,7 +3724,7 @@
         if (mNetworkRequests.get(nri.mRequests.get(0)) == null) {
             return;
         }
-        if (nri.getSatisfier() != null) {
+        if (nri.isBeingSatisfied()) {
             return;
         }
         if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) {
@@ -3799,7 +3823,7 @@
             // If there are still lingered requests on this network, don't tear it down,
             // but resume lingering instead.
             final long now = SystemClock.elapsedRealtime();
-            if (updateLingerState(nai, now)) {
+            if (updateInactivityState(nai, now)) {
                 notifyNetworkLosing(nai, now);
             }
             if (unneeded(nai, UnneededFor.TEARDOWN)) {
@@ -4260,7 +4284,7 @@
 
     @Override
     public NetworkRequest getDefaultRequest() {
-        return mFallbackRequest.mRequests.get(0);
+        return mDefaultRequest.mRequests.get(0);
     }
 
     private class InternalHandler extends Handler {
@@ -4506,7 +4530,7 @@
         // revalidate the network and generate a ConnectivityDiagnostics ConnectivityReport event.
         final NetworkAgentInfo nai;
         if (network == null) {
-            nai = getFallbackNetwork();
+            nai = getDefaultNetwork();
         } else {
             nai = getNetworkAgentInfoForNetwork(network);
         }
@@ -4525,7 +4549,7 @@
             Network network, int uid, boolean hasConnectivity) {
         final NetworkAgentInfo nai;
         if (network == null) {
-            nai = getFallbackNetwork();
+            nai = getDefaultNetwork();
         } else {
             nai = getNetworkAgentInfoForNetwork(network);
         }
@@ -4891,7 +4915,8 @@
         // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
         // the underlyingNetworks list.
         if (underlyingNetworks == null) {
-            final NetworkAgentInfo defaultNai = getFallbackNetwork();
+            final NetworkAgentInfo defaultNai = getDefaultNetworkForUid(
+                    nai.networkCapabilities.getOwnerUid());
             if (defaultNai != null) {
                 underlyingNetworks = new Network[] { defaultNai.network };
             }
@@ -4942,8 +4967,10 @@
         }
     }
 
-    private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) {
-        final Network defaultNetwork = getNetwork(getFallbackNetwork());
+    // TODO This needs to be the default network that applies to the NAI.
+    private Network[] underlyingNetworksOrDefault(final int ownerUid,
+            Network[] underlyingNetworks) {
+        final Network defaultNetwork = getNetwork(getDefaultNetworkForUid(ownerUid));
         if (underlyingNetworks == null && defaultNetwork != null) {
             // null underlying networks means to track the default.
             underlyingNetworks = new Network[] { defaultNetwork };
@@ -4956,7 +4983,8 @@
         // TODO: support more than one level of underlying networks, either via a fixed-depth search
         // (e.g., 2 levels of underlying networks), or via loop detection, or....
         if (!nai.supportsUnderlyingNetworks()) return false;
-        final Network[] underlying = underlyingNetworksOrDefault(nai.declaredUnderlyingNetworks);
+        final Network[] underlying = underlyingNetworksOrDefault(
+                nai.networkCapabilities.getOwnerUid(), nai.declaredUnderlyingNetworks);
         return ArrayUtils.contains(underlying, network);
     }
 
@@ -5414,27 +5442,21 @@
     private static class NetworkProviderInfo {
         public final String name;
         public final Messenger messenger;
-        private final AsyncChannel mAsyncChannel;
         private final IBinder.DeathRecipient mDeathRecipient;
         public final int providerId;
 
         NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
-                int providerId, IBinder.DeathRecipient deathRecipient) {
+                int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
             this.name = name;
             this.messenger = messenger;
             this.providerId = providerId;
-            mAsyncChannel = asyncChannel;
             mDeathRecipient = deathRecipient;
 
-            if ((mAsyncChannel == null) == (mDeathRecipient == null)) {
-                throw new AssertionError("Must pass exactly one of asyncChannel or deathRecipient");
+            if (mDeathRecipient == null) {
+                throw new AssertionError("Must pass a deathRecipient");
             }
         }
 
-        boolean isLegacyNetworkFactory() {
-            return mAsyncChannel != null;
-        }
-
         void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) {
             try {
                 messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
@@ -5445,38 +5467,19 @@
         }
 
         void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
-                        servingProviderId, request);
-            } else {
-                sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
+            sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
                             servingProviderId, request);
-            }
         }
 
         void cancelRequest(NetworkRequest request) {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request);
-            } else {
-                sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
-            }
+            sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
         }
 
         void connect(Context context, Handler handler) {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.connect(context, handler, messenger);
-            } else {
-                try {
-                    messenger.getBinder().linkToDeath(mDeathRecipient, 0);
-                } catch (RemoteException e) {
-                    mDeathRecipient.binderDied();
-                }
-            }
-        }
-
-        void completeConnection() {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+            try {
+                messenger.getBinder().linkToDeath(mDeathRecipient, 0);
+            } catch (RemoteException e) {
+                mDeathRecipient.binderDied();
             }
         }
     }
@@ -5512,9 +5515,8 @@
             mActiveRequest = activeRequest;
         }
 
-        // The network currently satisfying this request, or null if none. Must only be touched
-        // on the handler thread. This only makes sense for network requests and not for listens,
-        // as defined by NetworkRequest#isRequest(). For listens, this is always null.
+        // The network currently satisfying this NRI. Only one request in an NRI can have a
+        // satisfier. For non-multilayer requests, only REQUEST-type requests can have a satisfier.
         @Nullable
         private NetworkAgentInfo mSatisfier;
         NetworkAgentInfo getSatisfier() {
@@ -5537,6 +5539,18 @@
         final int mUid;
         final Messenger messenger;
 
+        /**
+         * Get the list of UIDs this nri applies to.
+         */
+        @NonNull
+        private Set<UidRange> getUids() {
+            // networkCapabilities.getUids() returns a defensive copy.
+            // multilayer requests will all have the same uids so return the first one.
+            final Set<UidRange> uids = null == mRequests.get(0).networkCapabilities.getUids()
+                    ? new ArraySet<>() : mRequests.get(0).networkCapabilities.getUids();
+            return uids;
+        }
+
         NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
             mRequests = initializeRequests(r);
             ensureAllNetworkRequestsHaveType(mRequests);
@@ -5570,6 +5584,13 @@
             this(r, null);
         }
 
+        // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
+        // set to the mNoServiceNetwork in which case mActiveRequest will be null thus returning
+        // false.
+        boolean isBeingSatisfied() {
+            return (null != mSatisfier && null != mActiveRequest);
+        }
+
         boolean isMultilayerRequest() {
             return mRequests.size() > 1;
         }
@@ -5595,7 +5616,9 @@
 
         @Override
         public String toString() {
-            return "uid/pid:" + mUid + "/" + mPid + " " + mRequests
+            return "uid/pid:" + mUid + "/" + mPid + " active request Id: "
+                    + (mActiveRequest == null ? null : mActiveRequest.requestId)
+                    + " " + mRequests
                     + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
         }
     }
@@ -5951,15 +5974,6 @@
                 EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest));
     }
 
-    @Override
-    public int registerNetworkFactory(Messenger messenger, String name) {
-        enforceNetworkFactoryPermission();
-        NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(),
-                nextNetworkProviderId(), null /* deathRecipient */);
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
-        return npi.providerId;
-    }
-
     private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
         if (mNetworkProviderInfos.containsKey(npi.messenger)) {
             // Avoid creating duplicates. even if an app makes a direct AIDL call.
@@ -5973,10 +5987,7 @@
         if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
         mNetworkProviderInfos.put(npi.messenger, npi);
         npi.connect(mContext, mTrackerHandler);
-        if (!npi.isLegacyNetworkFactory()) {
-            // Legacy NetworkFactories get their requests when their AsyncChannel connects.
-            sendAllRequestsToProvider(npi);
-        }
+        sendAllRequestsToProvider(npi);
     }
 
     @Override
@@ -5995,11 +6006,6 @@
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
     }
 
-    @Override
-    public void unregisterNetworkFactory(Messenger messenger) {
-        unregisterNetworkProvider(messenger);
-    }
-
     private void handleUnregisterNetworkProvider(Messenger messenger) {
         NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger);
         if (npi == null) {
@@ -6050,11 +6056,31 @@
     // The always-on request for an Internet-capable network that apps without a specific default
     // fall back to.
     @NonNull
-    private final NetworkRequestInfo mFallbackRequest;
+    private final NetworkRequestInfo mDefaultRequest;
     // Collection of NetworkRequestInfo's used for default networks.
     @NonNull
     private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
 
+    private boolean isPerAppDefaultRequest(@NonNull final NetworkRequestInfo nri) {
+        return (mDefaultNetworkRequests.contains(nri) && mDefaultRequest != nri);
+    }
+
+    /**
+     * Determine if an nri is a managed default request that disallows default networking.
+     * @param nri the request to evaluate
+     * @return true if device-default networking is disallowed
+     */
+    private boolean isDefaultBlocked(@NonNull final NetworkRequestInfo nri) {
+        // Check if this nri is a managed default that supports the default network at its
+        // lowest priority request.
+        final NetworkRequest defaultNetworkRequest = mDefaultRequest.mRequests.get(0);
+        final NetworkCapabilities lowestPriorityNetCap =
+                nri.mRequests.get(nri.mRequests.size() - 1).networkCapabilities;
+        return isPerAppDefaultRequest(nri)
+                && !(defaultNetworkRequest.networkCapabilities.equalRequestableCapabilities(
+                        lowestPriorityNetCap));
+    }
+
     // Request used to optionally keep mobile data active even when higher
     // priority networks like Wi-Fi are active.
     private final NetworkRequest mDefaultMobileDataRequest;
@@ -6066,10 +6092,37 @@
     // Request used to optionally keep vehicle internal network always active
     private final NetworkRequest mDefaultVehicleRequest;
 
-    // TODO: b/178729499 update this in favor of a method taking in a UID.
-    // The NetworkAgentInfo currently satisfying the fallback request, if any.
-    private NetworkAgentInfo getFallbackNetwork() {
-        return mFallbackRequest.mSatisfier;
+    // TODO replace with INetd.DUMMY_NET_ID when available.
+    private static final int NO_SERVICE_NET_ID = 51;
+    // Sentinel NAI used to direct apps with default networks that should have no connectivity to a
+    // network with no service. This NAI should never be matched against, nor should any public API
+    // ever return the associated network. For this reason, this NAI is not in the list of available
+    // NAIs. It is used in computeNetworkReassignment() to be set as the satisfier for non-device
+    // default requests that don't support using the device default network which will ultimately
+    // allow ConnectivityService to use this no-service network when calling makeDefaultForApps().
+    @VisibleForTesting
+    final NetworkAgentInfo mNoServiceNetwork;
+
+    // The NetworkAgentInfo currently satisfying the default request, if any.
+    private NetworkAgentInfo getDefaultNetwork() {
+        return mDefaultRequest.mSatisfier;
+    }
+
+    private NetworkAgentInfo getDefaultNetworkForUid(final int uid) {
+        for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+            // Currently, all network requests will have the same uids therefore checking the first
+            // one is sufficient. If/when uids are tracked at the nri level, this can change.
+            final Set<UidRange> uids = nri.mRequests.get(0).networkCapabilities.getUids();
+            if (null == uids) {
+                continue;
+            }
+            for (final UidRange range : uids) {
+                if (range.contains(uid)) {
+                    return nri.getSatisfier();
+                }
+            }
+        }
+        return getDefaultNetwork();
     }
 
     @Nullable
@@ -6086,8 +6139,8 @@
     }
 
     @VisibleForTesting
-    protected boolean isFallbackNetwork(NetworkAgentInfo nai) {
-        return nai == getFallbackNetwork();
+    protected boolean isDefaultNetwork(NetworkAgentInfo nai) {
+        return nai == getDefaultNetwork();
     }
 
     // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
@@ -6156,8 +6209,6 @@
 
         LinkProperties lp = new LinkProperties(linkProperties);
 
-        // TODO: Instead of passing mFallbackRequest, provide an API to determine whether a Network
-        // satisfies mFallbackRequest.
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         final NetworkAgentInfo nai = new NetworkAgentInfo(na,
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
@@ -6234,7 +6285,7 @@
 //        for (LinkProperties lp : newLp.getStackedLinks()) {
 //            updateMtu(lp, null);
 //        }
-        if (isFallbackNetwork(networkAgent)) {
+        if (isDefaultNetwork(networkAgent)) {
             updateTcpBufferSizes(newLp.getTcpBufferSizes());
         }
 
@@ -6246,7 +6297,7 @@
         // updateDnses will fetch the private DNS configuration from DnsManager.
         mDnsManager.updatePrivateDnsStatus(netId, newLp);
 
-        if (isFallbackNetwork(networkAgent)) {
+        if (isDefaultNetwork(networkAgent)) {
             handleApplyDefaultProxy(newLp.getHttpProxy());
         } else {
             updateProxy(newLp, oldLp);
@@ -6580,7 +6631,8 @@
     @VisibleForTesting
     void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
             @NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
-        underlyingNetworks = underlyingNetworksOrDefault(underlyingNetworks);
+        underlyingNetworks = underlyingNetworksOrDefault(
+                agentCaps.getOwnerUid(), underlyingNetworks);
         int[] transportTypes = agentCaps.getTransportTypes();
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
@@ -7186,7 +7238,7 @@
 
         // If we get here it means that the last linger timeout for this network expired. So there
         // must be no other active linger timers, and we must stop lingering.
-        oldNetwork.clearLingerState();
+        oldNetwork.clearInactivityState();
 
         if (unneeded(oldNetwork, UnneededFor.TEARDOWN)) {
             // Tear the network down.
@@ -7224,21 +7276,20 @@
             log("Switching to new default network for: " + nri + " using " + newDefaultNetwork);
         }
 
-        try {
-            // TODO http://b/176191930 update netd calls in follow-up CL for multinetwork changes.
-            if (mFallbackRequest != nri) {
-                return;
-            }
-
-            if (null != newDefaultNetwork) {
-                mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
-            } else {
-                mNetd.networkClearDefault();
-            }
-        } catch (RemoteException | ServiceSpecificException e) {
-            loge("Exception setting default network :" + e);
+        // Fix up the NetworkCapabilities of any networks that have this network as underlying.
+        if (newDefaultNetwork != null) {
+            propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
         }
 
+        // Set an app level managed default and return since further processing only applies to the
+        // default network.
+        if (mDefaultRequest != nri) {
+            makeDefaultForApps(nri, oldDefaultNetwork, newDefaultNetwork);
+            return;
+        }
+
+        makeDefaultNetwork(newDefaultNetwork);
+
         if (oldDefaultNetwork != null) {
             mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
         }
@@ -7249,10 +7300,6 @@
         updateTcpBufferSizes(null != newDefaultNetwork
                 ? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null);
         notifyIfacesChangedForNetworkStats();
-        // Fix up the NetworkCapabilities of any networks that have this network as underlying.
-        if (newDefaultNetwork != null) {
-            propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
-        }
 
         // Log 0 -> X and Y -> X default network transitions, where X is the new default.
         final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
@@ -7276,6 +7323,49 @@
                 prevNetwork, prevScore, prevLp, prevNc);
     }
 
+    private void makeDefaultForApps(@NonNull final NetworkRequestInfo nri,
+            @Nullable final NetworkAgentInfo oldDefaultNetwork,
+            @Nullable final NetworkAgentInfo newDefaultNetwork) {
+        try {
+            if (VDBG) {
+                log("Setting default network for " + nri
+                        + " using UIDs " + nri.getUids()
+                        + " with old network " + (oldDefaultNetwork != null
+                        ? oldDefaultNetwork.network().getNetId() : "null")
+                        + " and new network " + (newDefaultNetwork != null
+                        ? newDefaultNetwork.network().getNetId() : "null"));
+            }
+            if (nri.getUids().isEmpty()) {
+                throw new IllegalStateException("makeDefaultForApps called without specifying"
+                        + " any applications to set as the default." + nri);
+            }
+            if (null != newDefaultNetwork) {
+                mNetd.networkAddUidRanges(
+                        newDefaultNetwork.network.getNetId(),
+                        toUidRangeStableParcels(nri.getUids()));
+            }
+            if (null != oldDefaultNetwork) {
+                mNetd.networkRemoveUidRanges(
+                        oldDefaultNetwork.network.getNetId(),
+                        toUidRangeStableParcels(nri.getUids()));
+            }
+        } catch (RemoteException | ServiceSpecificException e) {
+            loge("Exception setting OEM network preference default network :" + e);
+        }
+    }
+
+    private void makeDefaultNetwork(@Nullable final NetworkAgentInfo newDefaultNetwork) {
+        try {
+            if (null != newDefaultNetwork) {
+                mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
+            } else {
+                mNetd.networkClearDefault();
+            }
+        } catch (RemoteException | ServiceSpecificException e) {
+            loge("Exception setting default network :" + e);
+        }
+    }
+
     private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
         // For consistency with previous behaviour, send onLost callbacks before onAvailable.
         processNewlyLostListenRequests(nai);
@@ -7397,9 +7487,9 @@
             @Nullable final NetworkAgentInfo previousSatisfier,
             @Nullable final NetworkAgentInfo newSatisfier,
             final long now) {
-        if (newSatisfier != null) {
+        if (null != newSatisfier && mNoServiceNetwork != newSatisfier) {
             if (VDBG) log("rematch for " + newSatisfier.toShortString());
-            if (previousSatisfier != null) {
+            if (null != previousSatisfier && mNoServiceNetwork != previousSatisfier) {
                 if (VDBG || DDBG) {
                     log("   accepting network in place of " + previousSatisfier.toShortString());
                 }
@@ -7413,7 +7503,7 @@
                 Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
                         + newRequest);
             }
-        } else {
+        } else if (null != previousSatisfier) {
             if (DBG) {
                 log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
                         + " request " + previousRequest.requestId);
@@ -7464,7 +7554,11 @@
                     break;
                 }
             }
-            if (bestNetwork != nri.mSatisfier) {
+            if (null == bestNetwork && isDefaultBlocked(nri)) {
+                // Remove default networking if disallowed for managed default requests.
+                bestNetwork = mNoServiceNetwork;
+            }
+            if (nri.getSatisfier() != bestNetwork) {
                 // bestNetwork may be null if no network can satisfy this request.
                 changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
                         nri, nri.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork));
@@ -7557,7 +7651,7 @@
             // if the state has not changed : the source of truth is controlled with
             // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
             // called while rematching the individual networks above.
-            if (updateLingerState(nai, now)) {
+            if (updateInactivityState(nai, now)) {
                 lingeredNetworks.add(nai);
             }
         }
@@ -7584,7 +7678,7 @@
         // Tear down all unneeded networks.
         for (NetworkAgentInfo nai : mNetworkAgentInfos) {
             if (unneeded(nai, UnneededFor.TEARDOWN)) {
-                if (nai.getLingerExpiry() > 0) {
+                if (nai.getInactivityExpiry() > 0) {
                     // This network has active linger timers and no requests, but is not
                     // lingering. Linger it.
                     //
@@ -7592,7 +7686,7 @@
                     // and became unneeded due to another network improving its score to the
                     // point where this network will no longer be able to satisfy any requests
                     // even if it validates.
-                    if (updateLingerState(nai, now)) {
+                    if (updateInactivityState(nai, now)) {
                         notifyNetworkLosing(nai, now);
                     }
                 } else {
@@ -7624,34 +7718,34 @@
     private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
             @NonNull final NetworkReassignment changes,
             @NonNull final Collection<NetworkAgentInfo> nais) {
-        final NetworkReassignment.RequestReassignment fallbackReassignment =
-                changes.getReassignment(mFallbackRequest);
-        final NetworkAgentInfo oldFallbackNetwork =
-                null != fallbackReassignment ? fallbackReassignment.mOldNetwork : null;
-        final NetworkAgentInfo newFallbackNetwork =
-                null != fallbackReassignment ? fallbackReassignment.mNewNetwork : null;
+        final NetworkReassignment.RequestReassignment reassignmentOfDefault =
+                changes.getReassignment(mDefaultRequest);
+        final NetworkAgentInfo oldDefaultNetwork =
+                null != reassignmentOfDefault ? reassignmentOfDefault.mOldNetwork : null;
+        final NetworkAgentInfo newDefaultNetwork =
+                null != reassignmentOfDefault ? reassignmentOfDefault.mNewNetwork : null;
 
-        if (oldFallbackNetwork != newFallbackNetwork) {
+        if (oldDefaultNetwork != newDefaultNetwork) {
             // Maintain the illusion : since the legacy API only understands one network at a time,
             // if the default network changed, apps should see a disconnected broadcast for the
             // old default network before they see a connected broadcast for the new one.
-            if (oldFallbackNetwork != null) {
-                mLegacyTypeTracker.remove(oldFallbackNetwork.networkInfo.getType(),
-                        oldFallbackNetwork, true);
+            if (oldDefaultNetwork != null) {
+                mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
+                        oldDefaultNetwork, true);
             }
-            if (newFallbackNetwork != null) {
+            if (newDefaultNetwork != null) {
                 // The new default network can be newly null if and only if the old default
                 // network doesn't satisfy the default request any more because it lost a
                 // capability.
-                mDefaultInetConditionPublished = newFallbackNetwork.lastValidated ? 100 : 0;
+                mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0;
                 mLegacyTypeTracker.add(
-                        newFallbackNetwork.networkInfo.getType(), newFallbackNetwork);
+                        newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
                 // If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast
                 // to reflect the NetworkInfo of this new network. This broadcast has to be sent
                 // after the disconnect broadcasts above, but before the broadcasts sent by the
                 // legacy type tracker below.
                 // TODO : refactor this, it's too complex
-                notifyLockdownVpn(newFallbackNetwork);
+                notifyLockdownVpn(newDefaultNetwork);
             }
         }
 
@@ -7686,7 +7780,7 @@
         }
 
         // A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above,
-        // because usually there are no NetworkRequests it satisfies (e.g., mFallbackRequest
+        // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest
         // wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the
         // newNetwork to the tracker explicitly (it's a no-op if it has already been added).
         if (nai.isVPN()) {
@@ -7697,9 +7791,9 @@
     private void updateInetCondition(NetworkAgentInfo nai) {
         // Don't bother updating until we've graduated to validated at least once.
         if (!nai.everValidated) return;
-        // For now only update icons for the fallback connection.
+        // For now only update icons for the default connection.
         // TODO: Update WiFi and cellular icons separately. b/17237507
-        if (!isFallbackNetwork(nai)) return;
+        if (!isDefaultNetwork(nai)) return;
 
         int newInetCondition = nai.lastValidated ? 100 : 0;
         // Don't repeat publish.
@@ -7869,7 +7963,7 @@
 
     // Notify the requests on this NAI that the network is now lingered.
     private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) {
-        final int lingerTime = (int) (nai.getLingerExpiry() - now);
+        final int lingerTime = (int) (nai.getInactivityExpiry() - now);
         notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
     }
 
@@ -7967,8 +8061,8 @@
                 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
             }
             NetworkAgentInfo newDefaultAgent = null;
-            if (nai.isSatisfyingRequest(mFallbackRequest.mRequests.get(0).requestId)) {
-                newDefaultAgent = getFallbackNetwork();
+            if (nai.isSatisfyingRequest(mDefaultRequest.mRequests.get(0).requestId)) {
+                newDefaultAgent = mDefaultRequest.getSatisfier();
                 if (newDefaultAgent != null) {
                     intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
                             newDefaultAgent.networkInfo);
@@ -8016,9 +8110,14 @@
     private Network[] getDefaultNetworks() {
         ensureRunningOnConnectivityServiceThread();
         final ArrayList<Network> defaultNetworks = new ArrayList<>();
-        final NetworkAgentInfo fallbackNetwork = getFallbackNetwork();
+        final Set<Integer> activeNetIds = new ArraySet<>();
+        for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+            if (nri.isBeingSatisfied()) {
+                activeNetIds.add(nri.getSatisfier().network().netId);
+            }
+        }
         for (NetworkAgentInfo nai : mNetworkAgentInfos) {
-            if (nai.everConnected && (nai == fallbackNetwork || nai.isVPN())) {
+            if (nai.everConnected && (activeNetIds.contains(nai.network().netId) || nai.isVPN())) {
                 defaultNetworks.add(nai.network);
             }
         }
@@ -8350,7 +8449,7 @@
             throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
         }
 
-        final int uid = InetDiagMessage.getConnectionOwnerUid(connectionInfo.protocol,
+        final int uid = mDeps.getConnectionOwnerUid(connectionInfo.protocol,
                 connectionInfo.local, connectionInfo.remote);
 
         /* Filter out Uids not associated with the VPN. */
diff --git a/services/core/java/com/android/server/LockGuard.java b/services/core/java/com/android/server/LockGuard.java
index 5ce16c4..b894f34 100644
--- a/services/core/java/com/android/server/LockGuard.java
+++ b/services/core/java/com/android/server/LockGuard.java
@@ -22,8 +22,6 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
-import com.android.internal.os.BackgroundThread;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
@@ -74,8 +72,9 @@
     public static final int INDEX_PACKAGES = 3;
     public static final int INDEX_STORAGE = 4;
     public static final int INDEX_WINDOW = 5;
-    public static final int INDEX_ACTIVITY = 6;
-    public static final int INDEX_DPMS = 7;
+    public static final int INDEX_PROC = 6;
+    public static final int INDEX_ACTIVITY = 7;
+    public static final int INDEX_DPMS = 8;
 
     private static Object[] sKnownFixed = new Object[INDEX_DPMS + 1];
 
@@ -229,6 +228,7 @@
             case INDEX_PACKAGES: return "PACKAGES";
             case INDEX_STORAGE: return "STORAGE";
             case INDEX_WINDOW: return "WINDOW";
+            case INDEX_PROC: return "PROCESS";
             case INDEX_ACTIVITY: return "ACTIVITY";
             case INDEX_DPMS: return "DPMS";
             default: return Integer.toString(index);
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 6a72010..4dce59f 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -66,6 +66,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -290,8 +291,10 @@
         public Vcn newVcn(
                 @NonNull VcnContext vcnContext,
                 @NonNull ParcelUuid subscriptionGroup,
-                @NonNull VcnConfig config) {
-            return new Vcn(vcnContext, subscriptionGroup, config);
+                @NonNull VcnConfig config,
+                @NonNull TelephonySubscriptionSnapshot snapshot,
+                @NonNull VcnSafemodeCallback safemodeCallback) {
+            return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback);
         }
 
         /** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -382,6 +385,7 @@
                 // delay)
                 for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
                     final VcnConfig config = mConfigs.get(entry.getKey());
+
                     if (config == null
                             || !snapshot.packageHasPermissionsForSubscriptionGroup(
                                     entry.getKey(), config.getProvisioningPackageName())) {
@@ -395,10 +399,13 @@
                                 // correct instance is torn down. This could happen as a result of a
                                 // Carrier App manually removing/adding a VcnConfig.
                                 if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
-                                    mVcns.remove(uuidToTeardown).teardownAsynchronously();
+                                    stopVcnLocked(uuidToTeardown);
                                 }
                             }
                         }, instanceToTeardown, CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+                    } else {
+                        // If this VCN's status has not changed, update it with the new snapshot
+                        entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
                     }
                 }
             }
@@ -406,14 +413,44 @@
     }
 
     @GuardedBy("mLock")
+    private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) {
+        final Vcn vcnToTeardown = mVcns.remove(uuidToTeardown);
+        if (vcnToTeardown == null) {
+            return;
+        }
+
+        vcnToTeardown.teardownAsynchronously();
+
+        // Now that the VCN is removed, notify all registered listeners to refresh their
+        // UnderlyingNetworkPolicy.
+        notifyAllPolicyListenersLocked();
+    }
+
+    @GuardedBy("mLock")
+    private void notifyAllPolicyListenersLocked() {
+        for (final PolicyListenerBinderDeath policyListener : mRegisteredPolicyListeners.values()) {
+            Binder.withCleanCallingIdentity(() -> policyListener.mListener.onPolicyChanged());
+        }
+    }
+
+    @GuardedBy("mLock")
     private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
         Slog.v(TAG, "Starting VCN config for subGrp: " + subscriptionGroup);
 
         // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
         //                    VCN.
 
-        final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config);
+        final VcnSafemodeCallbackImpl safemodeCallback =
+                new VcnSafemodeCallbackImpl(subscriptionGroup);
+
+        final Vcn newInstance =
+                mDeps.newVcn(
+                        mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback);
         mVcns.put(subscriptionGroup, newInstance);
+
+        // Now that a new VCN has started, notify all registered listeners to refresh their
+        // UnderlyingNetworkPolicy.
+        notifyAllPolicyListenersLocked();
     }
 
     @GuardedBy("mLock")
@@ -476,9 +513,7 @@
             synchronized (mLock) {
                 mConfigs.remove(subscriptionGroup);
 
-                if (mVcns.containsKey(subscriptionGroup)) {
-                    mVcns.remove(subscriptionGroup).teardownAsynchronously();
-                }
+                stopVcnLocked(subscriptionGroup);
 
                 writeConfigsToDiskLocked();
             }
@@ -508,7 +543,7 @@
         }
     }
 
-    /** Get current configuration list for testing purposes */
+    /** Get current VCNs for testing purposes */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public Map<ParcelUuid, Vcn> getAllVcns() {
         synchronized (mLock) {
@@ -610,8 +645,8 @@
             synchronized (mLock) {
                 ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
 
-                // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode
-                if (mVcns.containsKey(subGroup)) {
+                Vcn vcn = mVcns.get(subGroup);
+                if (vcn != null && vcn.isActive()) {
                     isVcnManagedNetwork = true;
                 }
             }
@@ -623,4 +658,31 @@
 
         return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
     }
+
+    /** Callback for signalling when a Vcn has entered Safemode. */
+    public interface VcnSafemodeCallback {
+        /** Called by a Vcn to signal that it has entered Safemode. */
+        void onEnteredSafemode();
+    }
+
+    /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */
+    private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback {
+        @NonNull private final ParcelUuid mSubGroup;
+
+        private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) {
+            mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
+        }
+
+        @Override
+        public void onEnteredSafemode() {
+            synchronized (mLock) {
+                // Ignore if this subscription group doesn't exist anymore
+                if (!mVcns.containsKey(mSubGroup)) {
+                    return;
+                }
+
+                notifyAllPolicyListenersLocked();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7da4d64..a4ff230 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -19,6 +19,9 @@
 import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
 import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
+import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
+import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
 import static android.os.Process.NFC_UID;
@@ -48,7 +51,6 @@
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
-import android.app.ApplicationExitInfo;
 import android.app.BroadcastOptions;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
@@ -621,14 +623,14 @@
 
         final boolean callerFg;
         if (caller != null) {
-            final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
+            final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller);
             if (callerApp == null) {
                 throw new SecurityException(
                         "Unable to find app for caller " + caller
                         + " (pid=" + callingPid
                         + ") when starting service " + service);
             }
-            callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
+            callerFg = callerApp.mState.getSetSchedGroup() != ProcessList.SCHED_GROUP_BACKGROUND;
         } else {
             callerFg = true;
         }
@@ -656,7 +658,7 @@
         // If we're starting indirectly (e.g. from PendingIntent), figure out whether
         // we're launching into an app in a background state.  This keys off of the same
         // idleness state tracking as e.g. O+ background service start policy.
-        final boolean bgLaunch = !mAm.isUidActiveLocked(r.appInfo.uid);
+        final boolean bgLaunch = !mAm.isUidActiveLOSP(r.appInfo.uid);
 
         // If the app has strict background restrictions, we treat any bg service
         // start analogously to the legacy-app forced-restrictions case, regardless
@@ -727,7 +729,7 @@
         if (forcedStandby || (!r.startRequested && !fgRequired)) {
             // Before going further -- if this app is not allowed to start services in the
             // background, then at this point we aren't going to let it period.
-            final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
+            final int allowed = mAm.getAppStartModeLOSP(r.appInfo.uid, r.packageName,
                     r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
             if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                 Slog.w(TAG, "Background start not allowed: service "
@@ -752,7 +754,7 @@
                 }
                 // This app knows it is in the new model where this operation is not
                 // allowed, so tell it what has happened.
-                UidRecord uidRec = mAm.mProcessList.getUidRecordLocked(r.appInfo.uid);
+                UidRecord uidRec = mAm.mProcessList.getUidRecordLOSP(r.appInfo.uid);
                 return new ComponentName("?", "app is in background uid " + uidRec);
             }
         }
@@ -825,7 +827,7 @@
         if (!callerFg && !fgRequired && r.app == null
                 && mAm.mUserController.hasStartedUserState(r.userId)) {
             ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
-            if (proc == null || proc.getCurProcState() > ActivityManager.PROCESS_STATE_RECEIVER) {
+            if (proc == null || proc.mState.getCurProcState() > PROCESS_STATE_RECEIVER) {
                 // If this is not coming from a foreground caller, then we may want
                 // to delay the start if there are already other background services
                 // that are starting.  This is to avoid process start spam when lots
@@ -853,7 +855,7 @@
                 }
                 if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
                 addToStarting = true;
-            } else if (proc.getCurProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
+            } else if (proc.mState.getCurProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
                 // We slightly loosen when we will enqueue this new service as a background
                 // starting service we are waiting for, to also include processes that are
                 // currently running other services or receivers.
@@ -862,9 +864,9 @@
                         "Not delaying, but counting as bg: " + r);
             } else if (DEBUG_DELAYED_STARTS) {
                 StringBuilder sb = new StringBuilder(128);
-                sb.append("Not potential delay (state=").append(proc.getCurProcState())
-                        .append(' ').append(proc.adjType);
-                String reason = proc.makeAdjReason();
+                sb.append("Not potential delay (state=").append(proc.mState.getCurProcState())
+                        .append(' ').append(proc.mState.getAdjType());
+                String reason = proc.mState.makeAdjReason();
                 if (reason != null) {
                     sb.append(' ');
                     sb.append(reason);
@@ -1159,7 +1161,7 @@
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "stopService: " + service
                 + " type=" + resolvedType);
 
-        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
+        final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller);
         if (caller != null && callerApp == null) {
             throw new SecurityException(
                     "Unable to find app for caller " + caller
@@ -1195,7 +1197,7 @@
             for (int i = services.mServicesByInstanceName.size() - 1; i >= 0; i--) {
                 ServiceRecord service = services.mServicesByInstanceName.valueAt(i);
                 if (service.appInfo.uid == uid && service.startRequested) {
-                    if (mAm.getAppStartModeLocked(service.appInfo.uid, service.packageName,
+                    if (mAm.getAppStartModeLOSP(service.appInfo.uid, service.packageName,
                             service.appInfo.targetSdkVersion, -1, false, false, false)
                             != ActivityManager.APP_START_MODE_NORMAL) {
                         if (stopping == null) {
@@ -1625,13 +1627,13 @@
     }
 
     void foregroundServiceProcStateChangedLocked(UidRecord uidRec) {
-        ServiceMap smap = mServiceMap.get(UserHandle.getUserId(uidRec.uid));
+        ServiceMap smap = mServiceMap.get(UserHandle.getUserId(uidRec.getUid()));
         if (smap != null) {
             boolean changed = false;
             for (int j = smap.mActiveForegroundApps.size()-1; j >= 0; j--) {
                 ActiveForegroundApp active = smap.mActiveForegroundApps.valueAt(j);
-                if (active.mUid == uidRec.uid) {
-                    if (uidRec.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP) {
+                if (active.mUid == uidRec.getUid()) {
+                    if (uidRec.getCurProcState() <= PROCESS_STATE_TOP) {
                         if (!active.mAppOnTop) {
                             active.mAppOnTop = true;
                             changed = true;
@@ -1650,7 +1652,7 @@
     }
 
     private boolean appIsTopLocked(int uid) {
-        return mAm.getUidState(uid) <= ActivityManager.PROCESS_STATE_TOP;
+        return mAm.getUidStateLocked(uid) <= PROCESS_STATE_TOP;
     }
 
     /**
@@ -1682,13 +1684,13 @@
                     default:
                         mAm.enforcePermission(
                                 android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
-                                r.app.pid, r.appInfo.uid, "startForeground");
+                                r.app.getPid(), r.appInfo.uid, "startForeground");
                 }
             } else {
                 if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
                     mAm.enforcePermission(
                             android.Manifest.permission.FOREGROUND_SERVICE,
-                            r.app.pid, r.appInfo.uid, "startForeground");
+                            r.app.getPid(), r.appInfo.uid, "startForeground");
                 }
 
                 int manifestType = r.serviceInfo.getForegroundServiceType();
@@ -1729,6 +1731,7 @@
                         ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
             }
 
+            final ProcessServiceRecord psr = r.app.mServices;
             try {
                 boolean ignoreForeground = false;
                 final int mode = mAm.getAppOpsManager().checkOpNoThrow(
@@ -1758,7 +1761,7 @@
                                     + r.shortInstanceName);
                     // Back off of any foreground expectations around this service, since we've
                     // just turned down its fg request.
-                    updateServiceForegroundLocked(r.app, false);
+                    updateServiceForegroundLocked(psr, false);
                     ignoreForeground = true;
                 }
 
@@ -1771,7 +1774,7 @@
                                 + r.shortInstanceName;
                         Slog.w(TAG, msg);
                         showFgsBgRestrictedNotificationLocked(r);
-                        updateServiceForegroundLocked(r.app, true);
+                        updateServiceForegroundLocked(psr, true);
                         ignoreForeground = true;
                         if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
                                 r.appInfo.uid)) {
@@ -1802,10 +1805,12 @@
                                 active.mPackageName = r.packageName;
                                 active.mUid = r.appInfo.uid;
                                 active.mShownWhileScreenOn = mScreenOn;
-                                if (r.app != null && r.app.uidRecord != null) {
-                                    active.mAppOnTop = active.mShownWhileTop =
-                                            r.app.uidRecord.getCurProcState()
-                                                    <= ActivityManager.PROCESS_STATE_TOP;
+                                if (r.app != null) {
+                                    final UidRecord uidRec = r.app.getUidRecord();
+                                    if (uidRec != null) {
+                                        active.mAppOnTop = active.mShownWhileTop =
+                                                uidRec.getCurProcState() <= PROCESS_STATE_TOP;
+                                    }
                                 }
                                 active.mStartTime = active.mStartVisibleTime
                                         = SystemClock.elapsedRealtime();
@@ -1837,7 +1842,7 @@
                     }
                     postFgsNotificationLocked(r);
                     if (r.app != null) {
-                        updateServiceForegroundLocked(r.app, true);
+                        updateServiceForegroundLocked(psr, true);
                     }
                     getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
                     mAm.notifyPackageUse(r.serviceInfo.packageName,
@@ -1889,7 +1894,7 @@
                 mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
                 if (r.app != null) {
                     mAm.updateLruProcessLocked(r.app, false, null);
-                    updateServiceForegroundLocked(r.app, true);
+                    updateServiceForegroundLocked(r.app.mServices, true);
                 }
             }
             // Leave the time-to-display as already set: re-entering foreground mode will
@@ -2127,7 +2132,7 @@
         }
 
         private boolean isNotTop() {
-            return mProcessRecord.getCurProcState() != ActivityManager.PROCESS_STATE_TOP;
+            return mProcessRecord.mState.getCurProcState() != PROCESS_STATE_TOP;
         }
 
         private void incrementOpCount(int op, boolean allowed) {
@@ -2225,36 +2230,45 @@
         }
     }
 
-    private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
+    private void updateServiceForegroundLocked(ProcessServiceRecord psr, boolean oomAdj) {
         boolean anyForeground = false;
         int fgServiceTypes = 0;
-        for (int i = proc.numberOfRunningServices() - 1; i >= 0; i--) {
-            ServiceRecord sr = proc.getRunningServiceAt(i);
+        for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
+            ServiceRecord sr = psr.getRunningServiceAt(i);
             if (sr.isForeground || sr.fgRequired) {
                 anyForeground = true;
                 fgServiceTypes |= sr.foregroundServiceType;
             }
         }
-        mAm.updateProcessForegroundLocked(proc, anyForeground, fgServiceTypes, oomAdj);
+        mAm.updateProcessForegroundLocked(psr.mApp, anyForeground, fgServiceTypes, oomAdj);
     }
 
-    private void updateAllowlistManagerLocked(ProcessRecord proc) {
-        proc.mAllowlistManager = false;
-        for (int i = proc.numberOfRunningServices() - 1; i >= 0; i--) {
-            ServiceRecord sr = proc.getRunningServiceAt(i);
+    private void updateAllowlistManagerLocked(ProcessServiceRecord psr) {
+        psr.mAllowlistManager = false;
+        for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
+            ServiceRecord sr = psr.getRunningServiceAt(i);
             if (sr.whitelistManager) {
-                proc.mAllowlistManager = true;
+                psr.mAllowlistManager = true;
                 break;
             }
         }
     }
 
-    public void updateServiceConnectionActivitiesLocked(ProcessRecord clientProc) {
+    private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) {
+        final ProcessServiceRecord psr = service.app.mServices;
+        psr.stopService(service);
+        psr.updateBoundClientUids();
+        if (service.whitelistManager) {
+            updateAllowlistManagerLocked(psr);
+        }
+    }
+
+    void updateServiceConnectionActivitiesLocked(ProcessServiceRecord clientPsr) {
         ArraySet<ProcessRecord> updatedProcesses = null;
-        for (int i = 0; i < clientProc.connections.size(); i++) {
-            final ConnectionRecord conn = clientProc.connections.valueAt(i);
+        for (int i = 0; i < clientPsr.numberOfConnections(); i++) {
+            final ConnectionRecord conn = clientPsr.getConnectionAt(i);
             final ProcessRecord proc = conn.binding.service.app;
-            if (proc == null || proc == clientProc) {
+            if (proc == null || proc == clientPsr.mApp) {
                 continue;
             } else if (updatedProcesses == null) {
                 updatedProcesses = new ArraySet<>();
@@ -2262,11 +2276,11 @@
                 continue;
             }
             updatedProcesses.add(proc);
-            updateServiceClientActivitiesLocked(proc, null, false);
+            updateServiceClientActivitiesLocked(proc.mServices, null, false);
         }
     }
 
-    private boolean updateServiceClientActivitiesLocked(ProcessRecord proc,
+    private boolean updateServiceClientActivitiesLocked(ProcessServiceRecord psr,
             ConnectionRecord modCr, boolean updateLru) {
         if (modCr != null && modCr.binding.client != null) {
             if (!modCr.binding.client.hasActivities()) {
@@ -2277,14 +2291,14 @@
         }
 
         boolean anyClientActivities = false;
-        for (int i = proc.numberOfRunningServices() - 1; i >= 0 && !anyClientActivities; i--) {
-            ServiceRecord sr = proc.getRunningServiceAt(i);
+        for (int i = psr.numberOfRunningServices() - 1; i >= 0 && !anyClientActivities; i--) {
+            ServiceRecord sr = psr.getRunningServiceAt(i);
             ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = sr.getConnections();
             for (int conni = connections.size() - 1; conni >= 0 && !anyClientActivities; conni--) {
                 ArrayList<ConnectionRecord> clist = connections.valueAt(conni);
                 for (int cri=clist.size()-1; cri>=0; cri--) {
                     ConnectionRecord cr = clist.get(cri);
-                    if (cr.binding.client == null || cr.binding.client == proc) {
+                    if (cr.binding.client == null || cr.binding.client == psr.mApp) {
                         // Binding to ourself is not interesting.
                         continue;
                     }
@@ -2295,10 +2309,10 @@
                 }
             }
         }
-        if (anyClientActivities != proc.hasClientActivities()) {
-            proc.setHasClientActivities(anyClientActivities);
+        if (anyClientActivities != psr.hasClientActivities()) {
+            psr.setHasClientActivities(anyClientActivities);
             if (updateLru) {
-                mAm.updateLruProcessLocked(proc, anyClientActivities, null);
+                mAm.updateLruProcessLocked(psr.mApp, anyClientActivities, null);
             }
             return true;
         }
@@ -2314,7 +2328,7 @@
                 + " flags=0x" + Integer.toHexString(flags));
         final int callingPid = Binder.getCallingPid();
         final int callingUid = Binder.getCallingUid();
-        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
+        final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller);
         if (callerApp == null) {
             throw new SecurityException(
                     "Unable to find app for caller " + caller
@@ -2386,7 +2400,8 @@
                     "BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND");
         }
 
-        final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
+        final boolean callerFg = callerApp.mState.getSetSchedGroup()
+                != ProcessList.SCHED_GROUP_BACKGROUND;
         final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
         final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
 
@@ -2440,7 +2455,7 @@
             }
 
             mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
-                    callerApp.getCurProcState(), s.appInfo.uid, s.appInfo.longVersionCode,
+                    callerApp.mState.getCurProcState(), s.appInfo.uid, s.appInfo.longVersionCode,
                     s.instanceName, s.processName);
             // Once the apps have become associated, if one of them is caller is ephemeral
             // the target app should now be able to see the calling app
@@ -2458,10 +2473,11 @@
             if (activity != null) {
                 activity.addConnection(c);
             }
-            b.client.connections.add(c);
+            final ProcessServiceRecord clientPsr = b.client.mServices;
+            clientPsr.addConnection(c);
             c.startAssociationIfNeeded();
             if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
-                b.client.hasAboveClient = true;
+                clientPsr.setHasAboveClient(true);
             }
             if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
                 s.whitelistManager = true;
@@ -2475,7 +2491,7 @@
             }
 
             if (s.app != null) {
-                updateServiceClientActivitiesLocked(s.app, c, true);
+                updateServiceClientActivitiesLocked(s.app.mServices, c, true);
             }
             ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
             if (clist == null) {
@@ -2497,17 +2513,18 @@
             setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, false);
 
             if (s.app != null) {
+                ProcessServiceRecord servicePsr = s.app.mServices;
                 if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
-                    s.app.treatLikeActivity = true;
+                    servicePsr.setTreatLikeActivity(true);
                 }
                 if (s.whitelistManager) {
-                    s.app.mAllowlistManager = true;
+                    servicePsr.mAllowlistManager = true;
                 }
                 // This could have made the service more important.
-                mAm.updateLruProcessLocked(s.app,
-                        (callerApp.hasActivitiesOrRecentTasks() && s.app.hasClientActivities())
-                                || (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP
-                                        && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
+                mAm.updateLruProcessLocked(s.app, (callerApp.hasActivitiesOrRecentTasks()
+                            && servicePsr.hasClientActivities())
+                        || (callerApp.mState.getCurProcState() <= PROCESS_STATE_TOP
+                            && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
                         b.client);
                 needOomAdj = true;
                 mAm.enqueueOomAdjTargetLocked(s.app);
@@ -2627,14 +2644,15 @@
             final ServiceRecord srec = crec.binding.service;
             if (srec != null && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
                 if (srec.app != null) {
+                    final ProcessServiceRecord psr = srec.app.mServices;
                     if (group > 0) {
-                        srec.app.connectionService = srec;
-                        srec.app.connectionGroup = group;
-                        srec.app.connectionImportance = importance;
+                        psr.setConnectionService(srec);
+                        psr.setConnectionGroup(group);
+                        psr.setConnectionImportance(importance);
                     } else {
-                        srec.app.connectionService = null;
-                        srec.app.connectionGroup = 0;
-                        srec.app.connectionImportance = 0;
+                        psr.setConnectionService(null);
+                        psr.setConnectionGroup(0);
+                        psr.setConnectionImportance(0);
                     }
                 } else {
                     if (group > 0) {
@@ -2672,15 +2690,14 @@
 
                 final ProcessRecord app = r.binding.service.app;
                 if (app != null) {
-                    if (app.mAllowlistManager) {
-                        updateAllowlistManagerLocked(app);
+                    final ProcessServiceRecord psr = app.mServices;
+                    if (psr.mAllowlistManager) {
+                        updateAllowlistManagerLocked(psr);
                     }
                     // This could have made the service less important.
                     if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
-                        app.treatLikeActivity = true;
-                        mAm.updateLruProcessLocked(app,
-                                app.hasClientActivities()
-                                || app.treatLikeActivity, null);
+                        psr.setTreatLikeActivity(true);
+                        mAm.updateLruProcessLocked(app, true, null);
                     }
                     mAm.enqueueOomAdjTargetLocked(app);
                 }
@@ -2714,7 +2731,7 @@
                         boolean inFg = false;
                         for (int i=b.apps.size()-1; i>=0; i--) {
                             ProcessRecord client = b.apps.valueAt(i).client;
-                            if (client != null && client.setSchedGroup
+                            if (client != null && client.mState.getSetSchedGroup()
                                     != ProcessList.SCHED_GROUP_BACKGROUND) {
                                 inFg = true;
                                 break;
@@ -3028,7 +3045,7 @@
         // happen.)
         boolean timeoutNeeded = true;
         if ((mAm.mBootPhase < SystemService.PHASE_THIRD_PARTY_APPS_CAN_START)
-                && (r.app != null) && (r.app.pid == android.os.Process.myPid())) {
+                && (r.app != null) && (r.app.getPid() == ActivityManagerService.MY_PID)) {
 
             Slog.w(TAG, "Too early to start/bind service in system_server: Phase=" + mAm.mBootPhase
                     + " " + r.getComponentName());
@@ -3036,6 +3053,7 @@
         }
 
         long now = SystemClock.uptimeMillis();
+        ProcessServiceRecord psr;
         if (r.executeNesting == 0) {
             r.executeFg = fg;
             ServiceState stracker = r.getTracker();
@@ -3043,16 +3061,20 @@
                 stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
             }
             if (r.app != null) {
-                r.app.executingServices.add(r);
-                r.app.execServicesFg |= fg;
-                if (timeoutNeeded && r.app.executingServices.size() == 1) {
+                psr = r.app.mServices;
+                psr.startExecutingService(r);
+                psr.setExecServicesFg(psr.shouldExecServicesFg() || fg);
+                if (timeoutNeeded && psr.numberOfExecutingServices() == 1) {
                     scheduleServiceTimeoutLocked(r.app);
                 }
             }
-        } else if (r.app != null && fg && !r.app.execServicesFg) {
-            r.app.execServicesFg = true;
-            if (timeoutNeeded) {
-                scheduleServiceTimeoutLocked(r.app);
+        } else if (r.app != null && fg) {
+            psr = r.app.mServices;
+            if (!psr.shouldExecServicesFg()) {
+                psr.setExecServicesFg(true);
+                if (timeoutNeeded) {
+                    scheduleServiceTimeoutLocked(r.app);
+                }
             }
         }
         r.executeFg |= fg;
@@ -3062,7 +3084,7 @@
 
     private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
             boolean execInFg, boolean rebind) throws TransactionTooLargeException {
-        if (r.app == null || r.app.thread == null) {
+        if (r.app == null || r.app.getThread() == null) {
             // If service is not currently running, can't yet bind.
             return false;
         }
@@ -3071,9 +3093,9 @@
         if ((!i.requested || rebind) && i.apps.size() > 0) {
             try {
                 bumpServiceExecutingLocked(r, execInFg, "bind");
-                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
-                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
-                        r.app.getReportedProcState());
+                r.app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
+                r.app.getThread().scheduleBindService(r, i.intent.getIntent(), rebind,
+                        r.app.mState.getReportedProcState());
                 if (!rebind) {
                     i.requested = true;
                 }
@@ -3296,7 +3318,7 @@
             boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen,
             boolean enqueueOomAdj)
             throws TransactionTooLargeException {
-        if (r.app != null && r.app.thread != null) {
+        if (r.app != null && r.app.getThread() != null) {
             sendServiceArgsLocked(r, execInFg, false);
             return null;
         }
@@ -3354,19 +3376,26 @@
             app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
             if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                         + " app=" + app);
-            if (app != null && app.thread != null) {
-                try {
-                    app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
-                    realStartServiceLocked(r, app, execInFg, enqueueOomAdj);
-                    return null;
-                } catch (TransactionTooLargeException e) {
-                    throw e;
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Exception when starting service " + r.shortInstanceName, e);
-                }
+            if (app != null) {
+                final IApplicationThread thread = app.getThread();
+                final int pid = app.getPid();
+                final UidRecord uidRecord = app.getUidRecord();
+                if (thread != null) {
+                    try {
+                        app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode,
+                                mAm.mProcessStats);
+                        realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
+                                enqueueOomAdj);
+                        return null;
+                    } catch (TransactionTooLargeException e) {
+                        throw e;
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Exception when starting service " + r.shortInstanceName, e);
+                    }
 
-                // If a dead object exception was thrown -- fall through to
-                // restart the application.
+                    // If a dead object exception was thrown -- fall through to
+                    // restart the application.
+                }
             }
         } else {
             // If this service runs in an isolated process, then each time
@@ -3391,8 +3420,8 @@
         if (app == null && !permissionsReviewRequired && !packageFrozen) {
             // TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service
             //  was initiated from a notification tap or not.
-            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
-                    hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
+            if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
+                        hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
                 String msg = "Unable to launch app "
                         + r.appInfo.packageName + "/"
                         + r.appInfo.uid + " for service "
@@ -3448,21 +3477,23 @@
      * The "start" here means bring up the instance in the client, and this method is called
      * from bindService() as well.
      */
-    private final void realStartServiceLocked(ServiceRecord r,
-            ProcessRecord app, boolean execInFg, boolean enqueueOomAdj) throws RemoteException {
-        if (app.thread == null) {
+    private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
+            IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
+            boolean enqueueOomAdj) throws RemoteException {
+        if (thread == null) {
             throw new RemoteException();
         }
         if (DEBUG_MU)
             Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
                     + ", ProcessRecord.uid = " + app.uid);
-        r.setProcess(app);
+        r.setProcess(app, thread, pid, uidRecord);
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
-        final boolean newService = app.startService(r);
+        final ProcessServiceRecord psr = app.mServices;
+        final boolean newService = psr.startService(r);
         bumpServiceExecutingLocked(r, execInFg, "create");
         mAm.updateLruProcessLocked(app, false, null);
-        updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
+        updateServiceForegroundLocked(psr, /* oomAdj= */ false);
         if (enqueueOomAdj) {
             mAm.enqueueOomAdjTargetLocked(app);
         } else {
@@ -3477,7 +3508,7 @@
                 nameTerm = lastPeriod >= 0 ? r.shortInstanceName.substring(lastPeriod)
                         : r.shortInstanceName;
                 EventLogTags.writeAmCreateService(
-                        r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
+                        r.userId, System.identityHashCode(r), nameTerm, r.app.uid, pid);
             }
 
             final int uid = r.appInfo.uid;
@@ -3488,10 +3519,10 @@
             mAm.mBatteryStatsService.noteServiceStartLaunch(uid, packageName, serviceName);
             mAm.notifyPackageUse(r.serviceInfo.packageName,
                                  PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
-            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
-            app.thread.scheduleCreateService(r, r.serviceInfo,
+            app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
+            thread.scheduleCreateService(r, r.serviceInfo,
                     mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
-                    app.getReportedProcState());
+                    app.mState.getReportedProcState());
             r.postNotification();
             created = true;
         } catch (DeadObjectException e) {
@@ -3506,8 +3537,8 @@
 
                 // Cleanup.
                 if (newService) {
-                    app.stopService(r);
-                    r.setProcess(null);
+                    psr.stopService(r);
+                    r.setProcess(null, null, 0, null);
                 }
 
                 // Retry.
@@ -3518,15 +3549,15 @@
         }
 
         if (r.whitelistManager) {
-            app.mAllowlistManager = true;
+            psr.mAllowlistManager = true;
         }
 
         requestServiceBindingsLocked(r, execInFg);
 
-        updateServiceClientActivitiesLocked(app, null, true);
+        updateServiceClientActivitiesLocked(psr, null, true);
 
         if (newService && created) {
-            app.addBoundClientUidsOfNewService(r);
+            psr.addBoundClientUidsOfNewService(r);
         }
 
         // If the service is in the started state, and there are no
@@ -3620,7 +3651,7 @@
         slice.setInlineCountLimit(4);
         Exception caughtException = null;
         try {
-            r.app.thread.scheduleServiceArgs(r, slice);
+            r.app.getThread().scheduleServiceArgs(r, slice);
         } catch (TransactionTooLargeException e) {
             if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size()
                     + " args, first: " + args.get(0).args);
@@ -3712,7 +3743,7 @@
 
         boolean needOomAdj = false;
         // Tell the service that it has been unbound.
-        if (r.app != null && r.app.thread != null) {
+        if (r.app != null && r.app.getThread() != null) {
             for (int i = r.bindings.size() - 1; i >= 0; i--) {
                 IntentBindRecord ibr = r.bindings.valueAt(i);
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing down binding " + ibr
@@ -3723,7 +3754,7 @@
                         needOomAdj = true;
                         ibr.hasBound = false;
                         ibr.requested = false;
-                        r.app.thread.scheduleUnbindService(r,
+                        r.app.getThread().scheduleUnbindService(r,
                                 ibr.intent.getIntent());
                     } catch (Exception e) {
                         Slog.w(TAG, "Exception when unbinding service "
@@ -3770,7 +3801,7 @@
         r.destroyTime = SystemClock.uptimeMillis();
         if (LOG_SERVICE_START_STOP) {
             EventLogTags.writeAmDestroyService(
-                    r.userId, System.identityHashCode(r), (r.app != null) ? r.app.pid : -1);
+                    r.userId, System.identityHashCode(r), (r.app != null) ? r.app.getPid() : -1);
         }
 
         final ServiceMap smap = getServiceMapLocked(r.userId);
@@ -3832,20 +3863,15 @@
         if (r.app != null) {
             mAm.mBatteryStatsService.noteServiceStopLaunch(r.appInfo.uid, r.name.getPackageName(),
                     r.name.getClassName());
-            r.app.stopService(r);
-            r.app.updateBoundClientUids();
-            if (r.whitelistManager) {
-                updateAllowlistManagerLocked(r.app);
-            }
-            if (r.app.thread != null) {
-                updateServiceForegroundLocked(r.app, false);
+            stopServiceAndUpdateAllowlistManagerLocked(r);
+            if (r.app.getThread() != null) {
+                updateServiceForegroundLocked(r.app.mServices, false);
                 try {
                     bumpServiceExecutingLocked(r, false, "destroy");
                     mDestroyingServices.add(r);
                     r.destroying = true;
-                    mAm.updateOomAdjLocked(r.app, true,
-                            OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
-                    r.app.thread.scheduleStopService(r);
+                    needOomAdj = true;
+                    r.app.getThread().scheduleStopService(r);
                 } catch (Exception e) {
                     Slog.w(TAG, "Exception when destroying service "
                             + r.shortInstanceName, e);
@@ -3907,16 +3933,17 @@
             c.activity.removeConnection(c);
         }
         if (b.client != skipApp) {
-            b.client.connections.remove(c);
+            final ProcessServiceRecord psr = b.client.mServices;
+            psr.removeConnection(c);
             if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
-                b.client.updateHasAboveClientLocked();
+                psr.updateHasAboveClientLocked();
             }
             // If this connection requested whitelist management, see if we should
             // now clear that state.
             if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
                 s.updateWhitelistManager();
                 if (!s.whitelistManager && s.app != null) {
-                    updateAllowlistManagerLocked(s.app);
+                    updateAllowlistManagerLocked(s.app.mServices);
                 }
             }
             // And do the same for bg activity starts ability.
@@ -3927,7 +3954,7 @@
                 s.updateIsAllowedBgFgsStartsByBinding();
             }
             if (s.app != null) {
-                updateServiceClientActivitiesLocked(s.app, c, true);
+                updateServiceClientActivitiesLocked(s.app.mServices, c, true);
             }
         }
         clist = mServiceConnections.get(binder);
@@ -3948,12 +3975,12 @@
         if (!c.serviceDead) {
             if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Disconnecting binding " + b.intent
                     + ": shouldUnbind=" + b.intent.hasBound);
-            if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
+            if (s.app != null && s.app.getThread() != null && b.intent.apps.size() == 0
                     && b.intent.hasBound) {
                 try {
                     bumpServiceExecutingLocked(s, false, "unbind");
                     if (b.client != s.app && (c.flags&Context.BIND_WAIVE_PRIORITY) == 0
-                            && s.app.setProcState <= ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
+                            && s.app.mState.getSetProcState() <= PROCESS_STATE_HEAVY_WEIGHT) {
                         // If this service's process is not already in the cached list,
                         // then update it in the LRU list here because this may be causing
                         // it to go down there and we want it to start out near the top.
@@ -3969,7 +3996,7 @@
                     // Assume the client doesn't want to know about a rebind;
                     // we will deal with that later if it asks for one.
                     b.intent.doRebind = false;
-                    s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
+                    s.app.getThread().scheduleUnbindService(s, b.intent.intent.getIntent());
                 } catch (Exception e) {
                     Slog.w(TAG, "Exception when unbinding service " + s.shortInstanceName, e);
                     serviceProcessGoneLocked(s, enqueueOomAdj);
@@ -4100,19 +4127,20 @@
         r.executeNesting--;
         if (r.executeNesting <= 0) {
             if (r.app != null) {
+                final ProcessServiceRecord psr = r.app.mServices;
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                         "Nesting at 0 of " + r.shortInstanceName);
-                r.app.execServicesFg = false;
-                r.app.executingServices.remove(r);
-                if (r.app.executingServices.size() == 0) {
+                psr.setExecServicesFg(false);
+                psr.stopExecutingService(r);
+                if (psr.numberOfExecutingServices() == 0) {
                     if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
                             "No more executingServices of " + r.shortInstanceName);
                     mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
                 } else if (r.executeFg) {
                     // Need to re-evaluate whether the app still needs to be in the foreground.
-                    for (int i=r.app.executingServices.size()-1; i>=0; i--) {
-                        if (r.app.executingServices.valueAt(i).executeFg) {
-                            r.app.execServicesFg = true;
+                    for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
+                        if (psr.getExecutingServiceAt(i).executeFg) {
+                            psr.setExecServicesFg(true);
                             break;
                         }
                     }
@@ -4142,13 +4170,9 @@
             }
             if (finishing) {
                 if (r.app != null && !r.app.isPersistent()) {
-                    r.app.stopService(r);
-                    r.app.updateBoundClientUids();
-                    if (r.whitelistManager) {
-                        updateAllowlistManagerLocked(r.app);
-                    }
+                    stopServiceAndUpdateAllowlistManagerLocked(r);
                 }
-                r.setProcess(null);
+                r.setProcess(null, null, 0, null);
             }
         }
     }
@@ -4167,11 +4191,15 @@
                         continue;
                     }
 
+                    final IApplicationThread thread = proc.getThread();
+                    final int pid = proc.getPid();
+                    final UidRecord uidRecord = proc.getUidRecord();
                     mPendingServices.remove(i);
                     i--;
                     proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode,
                             mAm.mProcessStats);
-                    realStartServiceLocked(sr, proc, sr.createdFromFg, true);
+                    realStartServiceLocked(sr, proc, thread, pid, uidRecord, sr.createdFromFg,
+                            true);
                     didSomething = true;
                     if (!isServiceNeededLocked(sr, false, false)) {
                         // We were waiting for this service to start, but it is actually no
@@ -4246,13 +4274,9 @@
                 didSomething = true;
                 Slog.i(TAG, "  Force stopping service " + service);
                 if (service.app != null && !service.app.isPersistent()) {
-                    service.app.stopService(service);
-                    service.app.updateBoundClientUids();
-                    if (service.whitelistManager) {
-                        updateAllowlistManagerLocked(service.app);
-                    }
+                    stopServiceAndUpdateAllowlistManagerLocked(service);
                 }
-                service.setProcess(null);
+                service.setProcess(null, null, 0, null);
                 service.isolatedProc = null;
                 if (mTmpCollectionResults == null) {
                     mTmpCollectionResults = new ArrayList<>();
@@ -4352,7 +4376,7 @@
                 } else {
                     sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
                             sr.getLastStartId(), baseIntent, null, 0));
-                    if (sr.app != null && sr.app.thread != null) {
+                    if (sr.app != null && sr.app.getThread() != null) {
                         // We always run in the foreground, since this is called as
                         // part of the "remove task" UI operation.
                         try {
@@ -4370,13 +4394,14 @@
     }
 
     final void killServicesLocked(ProcessRecord app, boolean allowRestart) {
+        final ProcessServiceRecord psr = app.mServices;
         // Report disconnected services.
         if (false) {
             // XXX we are letting the client link to the service for
             // death notifications.
-            int numberOfRunningServices = app.numberOfRunningServices();
+            int numberOfRunningServices = psr.numberOfRunningServices();
             for (int sIndex = 0; sIndex < numberOfRunningServices; sIndex++) {
-                ServiceRecord r = app.getRunningServiceAt(sIndex);
+                ServiceRecord r = psr.getRunningServiceAt(sIndex);
                 ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
                 for (int conni = connections.size() - 1; conni >= 0; conni--) {
                     ArrayList<ConnectionRecord> cl = connections.valueAt(conni);
@@ -4398,25 +4423,25 @@
         }
 
         // Clean up any connections this application has to other services.
-        for (int i = app.connections.size() - 1; i >= 0; i--) {
-            ConnectionRecord r = app.connections.valueAt(i);
+        for (int i = psr.numberOfConnections() - 1; i >= 0; i--) {
+            ConnectionRecord r = psr.getConnectionAt(i);
             removeConnectionLocked(r, app, null, true);
         }
-        updateServiceConnectionActivitiesLocked(app);
-        app.connections.clear();
+        updateServiceConnectionActivitiesLocked(psr);
+        psr.removeAllConnections();
 
-        app.mAllowlistManager = false;
+        psr.mAllowlistManager = false;
 
         // Clear app state from services.
-        for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
-            ServiceRecord sr = app.getRunningServiceAt(i);
+        for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
+            ServiceRecord sr = psr.getRunningServiceAt(i);
             mAm.mBatteryStatsService.noteServiceStopLaunch(sr.appInfo.uid, sr.name.getPackageName(),
                     sr.name.getClassName());
             if (sr.app != app && sr.app != null && !sr.app.isPersistent()) {
-                sr.app.stopService(sr);
-                sr.app.updateBoundClientUids();
+                sr.app.mServices.stopService(sr);
+                sr.app.mServices.updateBoundClientUids();
             }
-            sr.setProcess(null);
+            sr.setProcess(null, null, 0, null);
             sr.isolatedProc = null;
             sr.executeNesting = 0;
             sr.forceClearTracker();
@@ -4438,7 +4463,7 @@
                 for (int appi=b.apps.size()-1; appi>=0; appi--) {
                     final ProcessRecord proc = b.apps.keyAt(appi);
                     // If the process is already gone, skip it.
-                    if (proc.killedByAm || proc.thread == null) {
+                    if (proc.isKilledByAm() || proc.getThread() == null) {
                         continue;
                     }
                     // Only do this for processes that have an auto-create binding;
@@ -4446,7 +4471,7 @@
                     // service to restart.
                     final AppBindRecord abind = b.apps.valueAt(appi);
                     boolean hasCreate = false;
-                    for (int conni=abind.connections.size()-1; conni>=0; conni--) {
+                    for (int conni = abind.connections.size() - 1; conni >= 0; conni--) {
                         ConnectionRecord conn = abind.connections.valueAt(conni);
                         if ((conn.flags&(Context.BIND_AUTO_CREATE|Context.BIND_ALLOW_OOM_MANAGEMENT
                                 |Context.BIND_WAIVE_PRIORITY)) == Context.BIND_AUTO_CREATE) {
@@ -4458,13 +4483,15 @@
                         continue;
                     }
                     // XXX turned off for now until we have more time to get a better policy.
-                    if (false && proc != null && !proc.isPersistent() && proc.thread != null
-                            && proc.pid != 0 && proc.pid != ActivityManagerService.MY_PID
-                            && proc.setProcState >= ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
-                        proc.kill("bound to service " + sr.shortInstanceName
+                    /*
+                    if (false && proc != null && !proc.isPersistent() && proc.getThread() != null
+                            && proc.getPid() != 0 && proc.getPid() != ActivityManagerService.MY_PID
+                            && proc.mState.getSetProcState() >= PROCESS_STATE_LAST_ACTIVITY) {
+                        proc.killLocked("bound to service " + sr.shortInstanceName
                                 + " in dying proc " + (app != null ? app.processName : "??"),
                                 ApplicationExitInfo.REASON_OTHER, true);
                     }
+                    */
                 }
             }
         }
@@ -4472,14 +4499,14 @@
         ServiceMap smap = getServiceMapLocked(app.userId);
 
         // Now do remaining service cleanup.
-        for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
-            ServiceRecord sr = app.getRunningServiceAt(i);
+        for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
+            ServiceRecord sr = psr.getRunningServiceAt(i);
 
             // Unless the process is persistent, this process record is going away,
             // so make sure the service is cleaned out of it.
             if (!app.isPersistent()) {
-                app.stopService(sr);
-                app.updateBoundClientUids();
+                psr.stopService(sr);
+                psr.updateBoundClientUids();
             }
 
             // Sanity check: if the service listed for the app is not one
@@ -4501,7 +4528,7 @@
                 Slog.w(TAG, "Service crashed " + sr.crashCount
                         + " times, stopping: " + sr);
                 EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
-                        sr.userId, sr.crashCount, sr.shortInstanceName, app.pid);
+                        sr.userId, sr.crashCount, sr.shortInstanceName, sr.app.getPid());
                 bringDownServiceLocked(sr, true);
             } else if (!allowRestart
                     || !mAm.mUserController.isUserRunning(sr.userId, 0)) {
@@ -4530,8 +4557,8 @@
         mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
 
         if (!allowRestart) {
-            app.stopAllServices();
-            app.clearBoundClientUids();
+            psr.stopAllServices();
+            psr.clearBoundClientUids();
 
             // Make sure there are no more restarting services for this process.
             for (int i=mRestartingServices.size()-1; i>=0; i--) {
@@ -4570,7 +4597,7 @@
             }
         }
 
-        app.executingServices.clear();
+        psr.stopAllExecutingServices();
     }
 
     ActivityManager.RunningServiceInfo makeRunningServiceInfoLocked(ServiceRecord r) {
@@ -4578,7 +4605,7 @@
             new ActivityManager.RunningServiceInfo();
         info.service = r.name;
         if (r.app != null) {
-            info.pid = r.app.pid;
+            info.pid = r.app.getPid();
         }
         info.uid = r.appInfo.uid;
         info.process = r.processName;
@@ -4594,7 +4621,7 @@
         if (r.startRequested) {
             info.flags |= ActivityManager.RunningServiceInfo.FLAG_STARTED;
         }
-        if (r.app != null && r.app.pid == ActivityManagerService.MY_PID) {
+        if (r.app != null && r.app.getPid() == ActivityManagerService.MY_PID) {
             info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS;
         }
         if (r.app != null && r.app.isPersistent()) {
@@ -4693,16 +4720,17 @@
                 // The app's being debugged, ignore timeout.
                 return;
             }
-            if (proc.executingServices.size() == 0 || proc.thread == null) {
+            final ProcessServiceRecord psr = proc.mServices;
+            if (psr.numberOfExecutingServices() == 0 || proc.getThread() == null) {
                 return;
             }
             final long now = SystemClock.uptimeMillis();
             final long maxTime =  now -
-                    (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
+                    (psr.shouldExecServicesFg() ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
             ServiceRecord timeout = null;
             long nextTime = 0;
-            for (int i=proc.executingServices.size()-1; i>=0; i--) {
-                ServiceRecord sr = proc.executingServices.valueAt(i);
+            for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
+                ServiceRecord sr = psr.getExecutingServiceAt(i);
                 if (sr.executingStart < maxTime) {
                     timeout = sr;
                     break;
@@ -4711,7 +4739,7 @@
                     nextTime = sr.executingStart;
                 }
             }
-            if (timeout != null && mAm.mProcessList.mLruProcesses.contains(proc)) {
+            if (timeout != null && mAm.mProcessList.isInLruListLOSP(proc)) {
                 Slog.w(TAG, "Timeout executing service: " + timeout);
                 StringWriter sw = new StringWriter();
                 PrintWriter pw = new FastPrintWriter(sw, false, 1024);
@@ -4726,7 +4754,7 @@
                 Message msg = mAm.mHandler.obtainMessage(
                         ActivityManagerService.SERVICE_TIMEOUT_MSG);
                 msg.obj = proc;
-                mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
+                mAm.mHandler.sendMessageAtTime(msg, psr.shouldExecServicesFg()
                         ? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
             }
         }
@@ -4780,24 +4808,24 @@
     }
 
     void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) {
-        mAm.crashApplication(app.uid, app.pid, app.info.packageName, app.userId,
+        mAm.crashApplication(app.uid, app.getPid(), app.info.packageName, app.userId,
                 "Context.startForegroundService() did not then call Service.startForeground(): "
                     + serviceRecord, false /*force*/);
     }
 
     void scheduleServiceTimeoutLocked(ProcessRecord proc) {
-        if (proc.executingServices.size() == 0 || proc.thread == null) {
+        if (proc.mServices.numberOfExecutingServices() == 0 || proc.getThread() == null) {
             return;
         }
         Message msg = mAm.mHandler.obtainMessage(
                 ActivityManagerService.SERVICE_TIMEOUT_MSG);
         msg.obj = proc;
-        mAm.mHandler.sendMessageDelayed(msg,
-                proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
+        mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
+                ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
     }
 
     void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
-        if (r.app.executingServices.size() == 0 || r.app.thread == null) {
+        if (r.app.mServices.numberOfExecutingServices() == 0 || r.app.getThread() == null) {
             return;
         }
         Message msg = mAm.mHandler.obtainMessage(
@@ -4994,7 +5022,7 @@
             if (proc == null) {
                 return;
             }
-            final IApplicationThread thread = proc.thread;
+            final IApplicationThread thread = proc.getThread();
             if (thread == null) {
                 return;
             }
@@ -5320,20 +5348,21 @@
             pw.print(Integer.toHexString(System.identityHashCode(r)));
             pw.print(" pid=");
             if (r.app != null) {
-                pw.print(r.app.pid);
+                pw.print(r.app.getPid());
                 pw.print(" user="); pw.println(r.userId);
             } else pw.println("(not running)");
             if (dumpAll) {
                 r.dump(pw, innerPrefix);
             }
         }
-        if (r.app != null && r.app.thread != null) {
+        IApplicationThread thread;
+        if (r.app != null && (thread = r.app.getThread()) != null) {
             pw.print(prefix); pw.println("  Client:");
             pw.flush();
             try {
                 TransferPipe tp = new TransferPipe();
                 try {
-                    r.app.thread.dumpService(tp.getWriteFd(), r, args);
+                    thread.dumpService(tp.getWriteFd(), r, args);
                     tp.setBufferPrefix(prefix + "    ");
                     tp.go(fd);
                 } finally {
@@ -5395,10 +5424,10 @@
             boolean allowBackgroundActivityStarts) {
         int ret = FGS_FEATURE_DENIED;
 
-        final int uidState = mAm.getUidState(callingUid);
+        final int uidState = mAm.getUidStateLocked(callingUid);
         if (ret == FGS_FEATURE_DENIED) {
             // Is the calling UID at PROCESS_STATE_TOP or above?
-            if (uidState <= ActivityManager.PROCESS_STATE_TOP) {
+            if (uidState <= PROCESS_STATE_TOP) {
                 ret = FGS_FEATURE_ALLOWED_BY_UID_STATE;
             }
         }
@@ -5439,14 +5468,16 @@
         }
 
         if (ret == FGS_FEATURE_DENIED) {
-            for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
-                final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
+            final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, pr -> {
                 if (pr.uid == callingUid) {
                     if (pr.getWindowProcessController().areBackgroundFgsStartsAllowed()) {
-                        ret = FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER;
-                        break;
+                        return FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER;
                     }
                 }
+                return null;
+            });
+            if (allowedType != null) {
+                ret = allowedType;
             }
         }
 
@@ -5501,46 +5532,37 @@
         int ret = allowWhileInUse;
 
         final StringBuilder sb = new StringBuilder(64);
-        final int uidState = mAm.getUidState(callingUid);
+        final int uidState = mAm.getUidStateLocked(callingUid);
         if (ret == FGS_FEATURE_DENIED) {
             // Is the calling UID at PROCESS_STATE_TOP or above?
-            if (uidState <= ActivityManager.PROCESS_STATE_TOP) {
+            if (uidState <= PROCESS_STATE_TOP) {
                 sb.append("uidState=").append(uidState);
                 ret = FGS_FEATURE_ALLOWED_BY_UID_STATE;
             }
         }
 
         if (ret == FGS_FEATURE_DENIED) {
-            for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
-                final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
-                if (pr.uid == callingUid) {
-                    if (pr.mAllowStartFgs) {
-                        ret = FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD;
-                        break;
-                    } else if (pr.isAllowedStartFgsState()) {
-                        ret = FGS_FEATURE_ALLOWED_BY_PROC_STATE;
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (ret == FGS_FEATURE_DENIED) {
-            for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
-                final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
-                if (pr.uid == callingUid) {
-                    if (pr.areBackgroundFgsStartsAllowedByToken()) {
-                        ret = FGS_FEATURE_ALLOWED_BY_FGS_BINDING;
-                        break;
+            final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, app -> {
+                if (app.uid == callingUid) {
+                    final ProcessStateRecord state = app.mState;
+                    if (state.getAllowedStartFgs() != FGS_FEATURE_DENIED) {
+                        return state.getAllowedStartFgs();
+                    } else if (state.isAllowedStartFgsState()) {
+                        return FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+                    } else if (state.areBackgroundFgsStartsAllowedByToken()) {
+                        return FGS_FEATURE_ALLOWED_BY_FGS_BINDING;
                     } else {
-                        final ActiveInstrumentation instr = pr.getActiveInstrumentation();
+                        final ActiveInstrumentation instr = app.getActiveInstrumentation();
                         if (instr != null
                                 && instr.mHasBackgroundForegroundServiceStartsPermission) {
-                            ret = FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION;
-                            break;
+                            return FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION;
                         }
                     }
                 }
+                return null;
+            });
+            if (allowedType != null) {
+                ret = allowedType;
             }
         }
 
@@ -5559,7 +5581,7 @@
         }
 
         if (ret == FGS_FEATURE_DENIED) {
-            if (mAm.isAllowlistedForFgsStartLocked(callingUid)) {
+            if (mAm.isAllowlistedForFgsStartLOSP(callingUid)) {
                 // uid is on DeviceIdleController's user/system allowlist
                 // or AMS's FgsStartTempAllowList.
                 ret = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
@@ -5635,7 +5657,7 @@
         return CompatChanges.isChangeEnabled(FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID, uid);
     }
 
-    private static String fgsCodeToString(@FgsFeatureRetCode int code) {
+    static String fgsCodeToString(@FgsFeatureRetCode int code) {
         switch (code) {
             case FGS_FEATURE_DENIED:
                 return "DENIED";
diff --git a/services/core/java/com/android/server/am/ActiveUids.java b/services/core/java/com/android/server/am/ActiveUids.java
index 27be53a..31db95b7 100644
--- a/services/core/java/com/android/server/am/ActiveUids.java
+++ b/services/core/java/com/android/server/am/ActiveUids.java
@@ -21,21 +21,29 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.CompositeRWLock;
+import com.android.internal.annotations.GuardedBy;
+
 import java.io.PrintWriter;
 
 /** Class for tracking active uids for running processes. */
 final class ActiveUids {
 
-    private ActivityManagerService mService;
+    private final ActivityManagerService mService;
+    private final ActivityManagerGlobalLock mProcLock;
 
-    private boolean mPostChangesToAtm;
+    private final boolean mPostChangesToAtm;
+
+    @CompositeRWLock({"mService", "mProcLock"})
     private final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
 
     ActiveUids(ActivityManagerService service, boolean postChangesToAtm) {
         mService = service;
+        mProcLock = service != null ? service.mProcLock : null;
         mPostChangesToAtm = postChangesToAtm;
     }
 
+    @GuardedBy({"mService", "mProcLock"})
     void put(int uid, UidRecord value) {
         mActiveUids.put(uid, value);
         if (mPostChangesToAtm) {
@@ -43,6 +51,7 @@
         }
     }
 
+    @GuardedBy({"mService", "mProcLock"})
     void remove(int uid) {
         mActiveUids.remove(uid);
         if (mPostChangesToAtm) {
@@ -50,38 +59,45 @@
         }
     }
 
+    @GuardedBy({"mService", "mProcLock"})
     void clear() {
         mActiveUids.clear();
         // It is only called for a temporal container with mPostChangesToAtm == false or test case.
         // So there is no need to notify activity task manager.
     }
 
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
     UidRecord get(int uid) {
         return mActiveUids.get(uid);
     }
 
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
     int size() {
         return mActiveUids.size();
     }
 
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
     UidRecord valueAt(int index) {
         return mActiveUids.valueAt(index);
     }
 
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
     int keyAt(int index) {
         return mActiveUids.keyAt(index);
     }
 
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
     int indexOfKey(int uid) {
         return mActiveUids.indexOfKey(uid);
     }
 
-    boolean dump(PrintWriter pw, String dumpPackage, int dumpAppId,
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean dump(final PrintWriter pw, String dumpPackage, int dumpAppId,
             String header, boolean needSep) {
         boolean printed = false;
         for (int i = 0; i < mActiveUids.size(); i++) {
             final UidRecord uidRec = mActiveUids.valueAt(i);
-            if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
+            if (dumpPackage != null && UserHandle.getAppId(uidRec.getUid()) != dumpAppId) {
                 continue;
             }
             if (!printed) {
@@ -91,16 +107,16 @@
                 }
                 pw.print("  "); pw.println(header);
             }
-            pw.print("    UID "); UserHandle.formatUid(pw, uidRec.uid);
+            pw.print("    UID "); UserHandle.formatUid(pw, uidRec.getUid());
             pw.print(": "); pw.println(uidRec);
-            pw.print("      curProcState="); pw.print(uidRec.mCurProcState);
+            pw.print("      curProcState="); pw.print(uidRec.getCurProcState());
             pw.print(" curCapability=");
-            ActivityManager.printCapabilitiesFull(pw, uidRec.curCapability);
+            ActivityManager.printCapabilitiesFull(pw, uidRec.getCurCapability());
             pw.println();
-            for (int j = uidRec.procRecords.size() - 1; j >= 0; j--) {
+            uidRec.forEachProcess(app -> {
                 pw.print("      proc=");
-                pw.println(uidRec.procRecords.valueAt(j));
-            }
+                pw.println(app);
+            });
         }
         return printed;
     }
@@ -108,7 +124,7 @@
     void dumpProto(ProtoOutputStream proto, String dumpPackage, int dumpAppId, long fieldId) {
         for (int i = 0; i < mActiveUids.size(); i++) {
             UidRecord uidRec = mActiveUids.valueAt(i);
-            if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
+            if (dumpPackage != null && UserHandle.getAppId(uidRec.getUid()) != dumpAppId) {
                 continue;
             }
             uidRec.dumpDebug(proto, fieldId);
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/services/core/java/com/android/server/am/ActivityManagerGlobalLock.java
similarity index 72%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to services/core/java/com/android/server/am/ActivityManagerGlobalLock.java
index 19b20f2..7823038 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/services/core/java/com/android/server/am/ActivityManagerGlobalLock.java
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
-package android.graphics.fonts;
+package com.android.server.am;
 
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/**
+ * Interface to mark whichever class with the implementation is considered as a global lock
+ * to be used in the package of ActivityManagerService.
+ */
+interface ActivityManagerGlobalLock {
+}
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/services/core/java/com/android/server/am/ActivityManagerProcLock.java
similarity index 63%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to services/core/java/com/android/server/am/ActivityManagerProcLock.java
index 19b20f2..3ef19ad 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/services/core/java/com/android/server/am/ActivityManagerProcLock.java
@@ -14,7 +14,15 @@
  * limitations under the License.
  */
 
-package android.graphics.fonts;
+package com.android.server.am;
 
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/**
+ * Class that is used to generate an instance of the {@link ActivityManagerService#mProcLock},
+ * so the CPU booster can identify the critical section.
+ *
+ * <p>
+ * Use "-Lpr" as the suffix of functions locked with this lock.
+ * </p>
+ */
+final class ActivityManagerProcLock implements ActivityManagerGlobalLock {
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cdf5c98..6ae0644 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -27,6 +27,7 @@
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
 import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
@@ -110,7 +111,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_OOM_ADJ;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -236,7 +236,6 @@
 import android.os.FactoryTest;
 import android.os.FileUtils;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.IDeviceIdentifiersPolicyService;
 import android.os.IPermissionController;
@@ -249,6 +248,7 @@
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
+import android.os.PowerWhitelistManager.TempAllowListType;
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
@@ -294,6 +294,7 @@
 import android.view.WindowManager;
 import android.view.autofill.AutofillManagerInternal;
 
+import com.android.internal.annotations.CompositeRWLock;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsActiveCallback;
@@ -396,7 +397,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 public class ActivityManagerService extends IActivityManager.Stub
-        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
+        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
 
     /**
      * Priority we boost main thread and RT of top app to.
@@ -416,7 +417,6 @@
     static final String TAG_NETWORK = TAG + POSTFIX_NETWORK;
     static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ;
     private static final String TAG_POWER = TAG + POSTFIX_POWER;
-    static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
     static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
     private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
@@ -527,12 +527,55 @@
 
     final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
 
+    @CompositeRWLock({"this", "mProcLock"})
     final ArrayList<ActiveInstrumentation> mActiveInstrumentation = new ArrayList<>();
 
     public final IntentFirewall mIntentFirewall;
 
     public OomAdjProfiler mOomAdjProfiler = new OomAdjProfiler();
 
+    /**
+     * The global lock for AMS, it's de-facto the ActivityManagerService object as of now.
+     */
+    final ActivityManagerGlobalLock mGlobalLock = ActivityManagerService.this;
+
+    /**
+     * Whether or not to enable the {@link #mProcLock}. If {@code false}, the {@link #mProcLock}
+     * will be equivalent to the {@link #mGlobalLock}.
+     */
+    private static final boolean ENABLE_PROC_LOCK = true;
+
+    /**
+     * The lock for process management.
+     *
+     * <p>
+     * This lock is widely used in conjunction with the {@link #mGlobalLock} at present,
+     * where it'll require any of the locks to read from a data class, and both of the locks
+     * to write into that data class.
+     *
+     * For the naming convention of function suffixes:
+     * <ul>
+     *    <li>-LOSP:    Locked with any Of global am Service or Process lock</li>
+     *    <li>-LSP:     Locked with both of global am Service and Process lock</li>
+     *    <li>-Locked:  Locked with global am service lock alone</li>
+     *    <li>-LPr:     Locked with Process lock alone</li>
+     * </ul>
+     * For the simplicity, the getters/setters of the fields in data classes usually don't end with
+     * the above suffixes even if they're guarded by the locks here.
+     * </p>
+     *
+     * <p>
+     * In terms of locking order, it should be right below to the {@link #mGlobalLock},
+     * and above everything else which used to be underneath the {@link #mGlobalLock}.
+     * As of today, the core components(services/providers/broadcasts) are still guarded by
+     * the {@link #mGlobalLock} alone, so be cautious, avoid from acquiring the {@link #mGlobalLock}
+     * while holding this lock.
+     * </p>
+     *
+     */
+    final ActivityManagerGlobalLock mProcLock = ENABLE_PROC_LOCK
+            ? new ActivityManagerProcLock() : mGlobalLock;
+
     // Whether we should use SCHED_FIFO for UI and RenderThreads.
     final boolean mUseFifoUiScheduling;
 
@@ -548,7 +591,10 @@
     // so that dispatch of foreground broadcasts gets precedence.
     final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[3];
 
+    @GuardedBy("this")
     BroadcastStats mLastBroadcastStats;
+
+    @GuardedBy("this")
     BroadcastStats mCurBroadcastStats;
 
     BroadcastQueue broadcastQueueForIntent(Intent intent) {
@@ -569,10 +615,11 @@
 
     /**
      * The package name of the DeviceOwner. This package is not permitted to have its data cleared.
+     * <p>Not actually used</p>
      */
-    String mDeviceOwnerName;
+    private volatile String mDeviceOwnerName;
 
-    private int mDeviceOwnerUid = Process.INVALID_UID;
+    private volatile int mDeviceOwnerUid = Process.INVALID_UID;
 
     /**
      * Map userId to its companion app uids.
@@ -643,6 +690,25 @@
         sThreadPriorityBooster.reset();
     }
 
+    private static ThreadPriorityBooster sProcThreadPriorityBooster = new ThreadPriorityBooster(
+            THREAD_PRIORITY_FOREGROUND, LockGuard.INDEX_PROC);
+
+    static void boostPriorityForProcLockedSection() {
+        if (ENABLE_PROC_LOCK) {
+            sProcThreadPriorityBooster.boost();
+        } else {
+            sThreadPriorityBooster.boost();
+        }
+    }
+
+    static void resetPriorityAfterProcLockedSection() {
+        if (ENABLE_PROC_LOCK) {
+            sProcThreadPriorityBooster.reset();
+        } else {
+            sThreadPriorityBooster.reset();
+        }
+    }
+
     /**
      * Process management.
      */
@@ -663,20 +729,23 @@
     /**
      * Non-persistent appId allowlist for background restrictions
      */
-    int[] mBackgroundAppIdAllowlist = new int[] {
+    @CompositeRWLock({"this", "mProcLock"})
+    private int[] mBackgroundAppIdAllowlist = new int[] {
             BLUETOOTH_UID
     };
 
     /**
      * Broadcast actions that will always be deliverable to unlaunched/background apps
      */
-    ArraySet<String> mBackgroundLaunchBroadcasts;
+    @GuardedBy("this")
+    private ArraySet<String> mBackgroundLaunchBroadcasts;
 
     /**
      * When an app has restrictions on the other apps that can have associations with it,
      * it appears here with a set of the allowed apps and also track debuggability of the app.
      */
-    ArrayMap<String, PackageAssociationInfo> mAllowedAssociations;
+    @GuardedBy("this")
+    private ArrayMap<String, PackageAssociationInfo> mAllowedAssociations;
 
     /**
      * Tracks association information for a particular package along with debuggability.
@@ -754,24 +823,24 @@
             return mPidMap.indexOfKey(key);
         }
 
-        void doAddInternal(ProcessRecord app) {
-            mPidMap.put(app.pid, app);
+        void doAddInternal(int pid, ProcessRecord app) {
+            mPidMap.put(pid, app);
         }
 
-        boolean doRemoveInternal(ProcessRecord app) {
-            final ProcessRecord existingApp = mPidMap.get(app.pid);
-            if (existingApp != null && existingApp.startSeq == app.startSeq) {
-                mPidMap.remove(app.pid);
+        boolean doRemoveInternal(int pid, ProcessRecord app) {
+            final ProcessRecord existingApp = mPidMap.get(pid);
+            if (existingApp != null && existingApp.getStartSeq() == app.getStartSeq()) {
+                mPidMap.remove(pid);
                 return true;
             }
             return false;
         }
 
-        boolean doRemoveIfNoThreadInternal(ProcessRecord app) {
-            if (app == null || app.thread != null) {
+        boolean doRemoveIfNoThreadInternal(int pid, ProcessRecord app) {
+            if (app == null || app.getThread() != null) {
                 return false;
             }
-            return doRemoveInternal(app);
+            return doRemoveInternal(pid, app);
         }
     }
 
@@ -782,18 +851,20 @@
      * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
      * method.
      */
+    @GuardedBy("this")
     void addPidLocked(ProcessRecord app) {
+        final int pid = app.getPid();
         synchronized (mPidsSelfLocked) {
-            mPidsSelfLocked.doAddInternal(app);
+            mPidsSelfLocked.doAddInternal(pid, app);
         }
         synchronized (sActiveProcessInfoSelfLocked) {
             if (app.processInfo != null) {
-                sActiveProcessInfoSelfLocked.put(app.pid, app.processInfo);
+                sActiveProcessInfoSelfLocked.put(pid, app.processInfo);
             } else {
-                sActiveProcessInfoSelfLocked.remove(app.pid);
+                sActiveProcessInfoSelfLocked.remove(pid);
             }
         }
-        mAtmInternal.onProcessMapped(app.pid, app.getWindowProcessController());
+        mAtmInternal.onProcessMapped(pid, app.getWindowProcessController());
     }
 
     /**
@@ -801,16 +872,17 @@
      * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
      * method.
      */
-    void removePidLocked(ProcessRecord app) {
+    @GuardedBy("this")
+    void removePidLocked(int pid, ProcessRecord app) {
         final boolean removed;
         synchronized (mPidsSelfLocked) {
-            removed = mPidsSelfLocked.doRemoveInternal(app);
+            removed = mPidsSelfLocked.doRemoveInternal(pid, app);
         }
         if (removed) {
             synchronized (sActiveProcessInfoSelfLocked) {
-                sActiveProcessInfoSelfLocked.remove(app.pid);
+                sActiveProcessInfoSelfLocked.remove(pid);
             }
-            mAtmInternal.onProcessUnMapped(app.pid);
+            mAtmInternal.onProcessUnMapped(pid);
         }
     }
 
@@ -819,16 +891,18 @@
      * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
      * method.
      */
-    boolean removePidIfNoThread(ProcessRecord app) {
+    @GuardedBy("this")
+    private boolean removePidIfNoThreadLocked(ProcessRecord app) {
         final boolean removed;
+        final int pid = app.getPid();
         synchronized (mPidsSelfLocked) {
-            removed = mPidsSelfLocked.doRemoveIfNoThreadInternal(app);
+            removed = mPidsSelfLocked.doRemoveIfNoThreadInternal(pid, app);
         }
         if (removed) {
             synchronized (sActiveProcessInfoSelfLocked) {
-                sActiveProcessInfoSelfLocked.remove(app.pid);
+                sActiveProcessInfoSelfLocked.remove(pid);
             }
-            mAtmInternal.onProcessUnMapped(app.pid);
+            mAtmInternal.onProcessUnMapped(pid);
         }
         return removed;
     }
@@ -865,6 +939,7 @@
             proto.end(pToken);
         }
     }
+    @GuardedBy("this")
     final SparseArray<ImportanceToken> mImportantProcesses = new SparseArray<ImportanceToken>();
 
     /**
@@ -872,12 +947,14 @@
      * system was ready.  We don't start them at that point, but ensure they
      * are started by the time booting is complete.
      */
+    @GuardedBy("this")
     final ArrayList<ProcessRecord> mProcessesOnHold = new ArrayList<ProcessRecord>();
 
     /**
      * List of persistent applications that are in the process
      * of being started.
      */
+    @GuardedBy("this")
     final ArrayList<ProcessRecord> mPersistentStartingProcesses = new ArrayList<ProcessRecord>();
 
     private final ActivityMetricsLaunchObserver mActivityLaunchObserver =
@@ -925,6 +1002,7 @@
      * Keeps track of all IIntentReceivers that have been registered for broadcasts.
      * Hash keys are the receiver IBinder, hash value is a ReceiverList.
      */
+    @GuardedBy("this")
     final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
 
     /**
@@ -977,6 +1055,7 @@
      * by the user ID the sticky is for, and can include UserHandle.USER_ALL
      * for stickies that are sent to all users.
      */
+    @GuardedBy("this")
     final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
             new SparseArray<ArrayMap<String, ArrayList<Intent>>>();
 
@@ -1016,6 +1095,7 @@
      * have seen.  Mapping is target uid -> target component -> source uid -> source process name
      * -> association data.
      */
+    @GuardedBy("this")
     final SparseArray<ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>>>
             mAssociations = new SparseArray<>();
     boolean mTrackingAssociations;
@@ -1067,16 +1147,19 @@
     /**
      * Power-save whitelisted app-ids (not including except-idle-whitelisted ones).
      */
+    @CompositeRWLock({"this", "mProcLock"})
     int[] mDeviceIdleAllowlist = new int[0];
 
     /**
      * Power-save whitelisted app-ids (including except-idle-whitelisted ones).
      */
+    @CompositeRWLock({"this", "mProcLock"})
     int[] mDeviceIdleExceptIdleAllowlist = new int[0];
 
     /**
      * Set of app ids that are temporarily allowed to escape bg check due to high-pri message
      */
+    @CompositeRWLock({"this", "mProcLock"})
     int[] mDeviceIdleTempAllowlist = new int[0];
 
     static final class PendingTempAllowlist {
@@ -1102,11 +1185,13 @@
         }
     }
 
+    @CompositeRWLock({"this", "mProcLock"})
     final PendingTempAllowlists mPendingTempAllowlist = new PendingTempAllowlists(this);
 
     /**
      * The temp-allowlist that is allowed to start FGS from background.
      */
+    @CompositeRWLock({"this", "mProcLock"})
     final FgsStartTempAllowList mFgsStartTempAllowList = new FgsStartTempAllowList();
 
     /**
@@ -1122,11 +1207,6 @@
     ArrayMap<String, IBinder> mAppBindArgs;
     ArrayMap<String, IBinder> mIsolatedAppBindArgs;
 
-    /**
-     * Temporary to avoid allocations.  Protected by main lock.
-     */
-    final StringBuilder mStringBuilder = new StringBuilder(256);
-
     volatile boolean mProcessesReady = false;
     volatile boolean mSystemReady = false;
     volatile boolean mOnBattery = false;
@@ -1147,6 +1227,7 @@
     /**
      * Last time (in uptime) at which we checked for power usage.
      */
+    @GuardedBy("mProcLock")
     long mLastPowerCheckUptime;
 
     /**
@@ -1162,10 +1243,14 @@
     /**
      * The uptime of the last time we performed idle maintenance.
      */
+    @GuardedBy("mProcLock")
     long mLastIdleTime = SystemClock.uptimeMillis();
 
     /**
      * For reporting to battery stats the current top application.
+     *
+     * <p>It has its own lock to avoid from the need of double locking if using the global
+     * ActivityManagerService lock and proc lock to guard it.</p>
      */
     @GuardedBy("mCurResumedAppLock")
     private String mCurResumedPackage = null;
@@ -1183,22 +1268,38 @@
      * service.  The ProcessMap is package/uid tuples; each of these contain
      * an array of the currently foreground processes.
      */
+    @GuardedBy("this")
     final ProcessMap<ArrayList<ProcessRecord>> mForegroundPackages
             = new ProcessMap<ArrayList<ProcessRecord>>();
 
     /**
      * Set if the systemServer made a call to enterSafeMode.
      */
+    @GuardedBy("this")
     boolean mSafeMode;
 
-    String mDebugApp = null;
-    boolean mWaitForDebugger = false;
-    boolean mDebugTransient = false;
-    String mOrigDebugApp = null;
-    boolean mOrigWaitForDebugger = false;
+    @GuardedBy("this")
+    private String mDebugApp = null;
+
+    @GuardedBy("this")
+    private boolean mWaitForDebugger = false;
+
+    @GuardedBy("this")
+    private boolean mDebugTransient = false;
+
+    @GuardedBy("this")
+    private String mOrigDebugApp = null;
+
+    @GuardedBy("this")
+    private boolean mOrigWaitForDebugger = false;
+
+    @GuardedBy("this")
     boolean mAlwaysFinishActivities = false;
 
-    String mTrackAllocationApp = null;
+    @GuardedBy("mProcLock")
+    private String mTrackAllocationApp = null;
+
+    @GuardedBy("this")
     String mNativeDebuggingApp = null;
 
     final Injector mInjector;
@@ -1343,10 +1444,12 @@
     /**
      * Whether to force background check on all apps (for battery saver) or not.
      */
-    boolean mForceBackgroundCheck;
+    @CompositeRWLock({"this", "mProcLock"})
+    private boolean mForceBackgroundCheck;
 
     private static String sTheRealBuildSerial = Build.UNKNOWN;
 
+    @GuardedBy("mProcLock")
     private ParcelFileDescriptor[] mLifeMonitorFds;
 
     static final HostingRecord sNullHostingRecord = new HostingRecord(null);
@@ -1368,6 +1471,7 @@
     /**
      * The last time when the binder heavy hitter auto sampler started.
      */
+    @GuardedBy("mProcLock")
     private long mLastBinderHeavyHitterAutoSamplerStart = 0L;
 
     final AppProfiler mAppProfiler;
@@ -1405,19 +1509,19 @@
                 } break;
                 case SHOW_STRICT_MODE_VIOLATION_UI_MSG: {
                     HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
-                    synchronized (ActivityManagerService.this) {
+                    synchronized (mProcLock) {
                         ProcessRecord proc = (ProcessRecord) data.get("app");
                         if (proc == null) {
                             Slog.e(TAG, "App not found when showing strict mode dialog.");
                             break;
                         }
-                        if (proc.getDialogController().hasViolationDialogs()) {
+                        if (proc.mErrorState.getDialogController().hasViolationDialogs()) {
                             Slog.e(TAG, "App already has strict mode dialog: " + proc);
                             return;
                         }
                         AppErrorResult res = (AppErrorResult) data.get("result");
                         if (mAtmInternal.showStrictModeViolationDialog()) {
-                            proc.getDialogController().showViolationDialogs(res);
+                            proc.mErrorState.getDialogController().showViolationDialogs(res);
                         } else {
                             // The device is asleep, so just pretend that the user
                             // saw a crash dialog and hit "force quit".
@@ -1427,15 +1531,15 @@
                     ensureBootCompleted();
                 } break;
                 case WAIT_FOR_DEBUGGER_UI_MSG: {
-                    synchronized (ActivityManagerService.this) {
+                    synchronized (mProcLock) {
                         ProcessRecord app = (ProcessRecord) msg.obj;
                         if (msg.arg1 != 0) {
-                            if (!app.waitedForDebugger) {
-                                app.getDialogController().showDebugWaitingDialogs();
-                                app.waitedForDebugger = true;
+                            if (!app.hasWaitedForDebugger()) {
+                                app.mErrorState.getDialogController().showDebugWaitingDialogs();
+                                app.setWaitedForDebugger(true);
                             }
                         } else {
-                            app.getDialogController().clearWaitingDialog();
+                            app.mErrorState.getDialogController().clearWaitingDialog();
                         }
                     }
                 } break;
@@ -1486,23 +1590,23 @@
                         msg.getData().getCharSequence(SERVICE_RECORD_KEY));
             } break;
             case UPDATE_TIME_ZONE: {
-                synchronized (ActivityManagerService.this) {
-                    for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
-                        ProcessRecord r = mProcessList.mLruProcesses.get(i);
-                        if (r.thread != null) {
+                synchronized (mProcLock) {
+                    mProcessList.forEachLruProcessesLOSP(false, app -> {
+                        final IApplicationThread thread = app.getThread();
+                        if (thread != null) {
                             try {
-                                r.thread.updateTimeZone();
+                                thread.updateTimeZone();
                             } catch (RemoteException ex) {
                                 Slog.w(TAG, "Failed to update time zone for: "
-                                        + r.info.processName);
+                                        + app.info.processName);
                             }
-                        }
+                            }
+                        });
                     }
-                }
             } break;
             case CLEAR_DNS_CACHE_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    mProcessList.clearAllDnsCacheLocked();
+                synchronized (mProcLock) {
+                    mProcessList.clearAllDnsCacheLOSP();
                 }
             } break;
             case UPDATE_HTTP_PROXY_MSG: {
@@ -1557,8 +1661,8 @@
             case UPDATE_TIME_PREFERENCE_MSG: {
                 // The user's time format preference might have changed.
                 // For convenience we re-use the Intent extra values.
-                synchronized (ActivityManagerService.this) {
-                    mProcessList.updateAllTimePrefsLocked(msg.arg1);
+                synchronized (mProcLock) {
+                    mProcessList.updateAllTimePrefsLOSP(msg.arg1);
                 }
                 break;
             }
@@ -1566,19 +1670,21 @@
                 final int uid = msg.arg1;
                 final byte[] firstPacket = (byte[]) msg.obj;
 
-                synchronized (mPidsSelfLocked) {
-                    for (int i = 0; i < mPidsSelfLocked.size(); i++) {
-                        final ProcessRecord p = mPidsSelfLocked.valueAt(i);
-                        if (p.uid == uid && p.thread != null) {
-                            try {
-                                p.thread.notifyCleartextNetwork(firstPacket);
-                            } catch (RemoteException ignored) {
+                synchronized (mProcLock) {
+                    synchronized (mPidsSelfLocked) {
+                        for (int i = 0; i < mPidsSelfLocked.size(); i++) {
+                            final ProcessRecord p = mPidsSelfLocked.valueAt(i);
+                            final IApplicationThread thread = p.getThread();
+                            if (p.uid == uid && thread != null) {
+                                try {
+                                    thread.notifyCleartextNetwork(firstPacket);
+                                } catch (RemoteException ignored) {
+                                }
                             }
                         }
                     }
                 }
-                break;
-            }
+            } break;
             case POST_DUMP_HEAP_NOTIFICATION_MSG: {
                 mAppProfiler.handlePostDumpHeapNotification();
             } break;
@@ -1600,8 +1706,8 @@
                 idleUids();
             } break;
             case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    mProcessList.handleAllTrustStorageUpdateLocked();
+                synchronized (mProcLock) {
+                    mProcessList.handleAllTrustStorageUpdateLOSP();
                 }
             } break;
                 case BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG: {
@@ -1641,12 +1747,12 @@
                         0,
                         new HostingRecord("system"));
                 app.setPersistent(true);
-                app.pid = MY_PID;
+                app.mPid = MY_PID;
                 app.getWindowProcessController().setPid(MY_PID);
-                app.maxAdj = ProcessList.SYSTEM_ADJ;
+                app.mState.setMaxAdj(ProcessList.SYSTEM_ADJ);
                 app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
                 addPidLocked(app);
-                mProcessList.updateLruProcessLocked(app, false, null);
+                updateLruProcessLocked(app, false, null);
                 updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
             }
         } catch (PackageManager.NameNotFoundException e) {
@@ -2099,19 +2205,11 @@
         mEnableOffloadQueue = SystemProperties.getBoolean(
                 "persist.device_config.activity_manager_native_boot.offload_queue_enabled", false);
 
-        // Decouple broadcast-related timing operations from other OS activity by
-        // using a dedicated thread.  Sharing this thread between queues is safe
-        // because we know the nature of the activity on it and can't stall
-        // unexpectedly.
-        HandlerThread broadcastThread = new HandlerThread("broadcast");
-        broadcastThread.start();
-        Handler broadcastHandler = broadcastThread.getThreadHandler();
-
-        mFgBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
+        mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                 "foreground", foreConstants, false);
-        mBgBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
+        mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
                 "background", backConstants, true);
-        mOffloadBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
+        mOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
                 "offload", offloadConstants, true);
         mBroadcastQueues[0] = mFgBroadcastQueue;
         mBroadcastQueues[1] = mBgBroadcastQueue;
@@ -2296,16 +2394,18 @@
         if (code == SYSPROPS_TRANSACTION) {
             // We need to tell all apps about the system property change.
             ArrayList<IBinder> procs = new ArrayList<IBinder>();
-            synchronized (this) {
-                final int NP = mProcessList.mProcessNames.getMap().size();
-                for (int ip = 0; ip < NP; ip++) {
-                    SparseArray<ProcessRecord> apps =
-                            mProcessList.mProcessNames.getMap().valueAt(ip);
-                    final int NA = apps.size();
-                    for (int ia = 0; ia < NA; ia++) {
+            synchronized (mProcLock) {
+                final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
+                        mProcessList.getProcessNamesLOSP().getMap();
+                final int numOfNames = pmap.size();
+                for (int ip = 0; ip < numOfNames; ip++) {
+                    SparseArray<ProcessRecord> apps = pmap.valueAt(ip);
+                    final int numOfApps = apps.size();
+                    for (int ia = 0; ia < numOfApps; ia++) {
                         ProcessRecord app = apps.valueAt(ia);
-                        if (app.thread != null) {
-                            procs.add(app.thread.asBinder());
+                        final IApplicationThread thread = app.getThread();
+                        if (thread != null) {
+                            procs.add(thread.asBinder());
                         }
                     }
                 }
@@ -2357,10 +2457,8 @@
         // When plugging in, update the CPU stats first before changing
         // the plug state.
         updateCpuStatsNow();
-        synchronized (this) {
-            synchronized(mPidsSelfLocked) {
-                mOnBattery = DEBUG_POWER ? true : onBattery;
-            }
+        synchronized (mProcLock) {
+            mOnBattery = DEBUG_POWER ? true : onBattery;
             mOomAdjProfiler.batteryPowerChanged(onBattery);
         }
     }
@@ -2455,21 +2553,25 @@
         mActivityTaskManager.unregisterTaskStackListener(listener);
     }
 
+    @GuardedBy("this")
     final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
             ProcessRecord client) {
         mProcessList.updateLruProcessLocked(app, activityChange, client);
     }
 
+    @GuardedBy("this")
     final void removeLruProcessLocked(ProcessRecord app) {
         mProcessList.removeLruProcessLocked(app);
     }
 
+    @GuardedBy("this")
     final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
         return mProcessList.getProcessRecordLocked(processName, uid, keepIfLarge);
     }
 
-    final ProcessMap<ProcessRecord> getProcessNames() {
-        return mProcessList.mProcessNames;
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    final ProcessMap<ProcessRecord> getProcessNamesLOSP() {
+        return mProcessList.getProcessNamesLOSP();
     }
 
     void notifyPackageUse(String packageName, int reason) {
@@ -2552,7 +2654,7 @@
         if (mUsageStatsService != null) {
             mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode(), taskRoot);
         }
-        final ContentCaptureManagerInternal contentCaptureService = mContentCaptureService;
+        ContentCaptureManagerInternal contentCaptureService = mContentCaptureService;
         if (contentCaptureService != null && (event == Event.ACTIVITY_PAUSED
                 || event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED
                 || event == Event.ACTIVITY_DESTROYED)) {
@@ -2626,19 +2728,18 @@
                     "getPackageProcessState");
         }
 
-        int procState = PROCESS_STATE_NONEXISTENT;
-        synchronized (this) {
-            for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
-                final ProcessRecord proc = mProcessList.mLruProcesses.get(i);
-                if (procState > proc.setProcState) {
-                    if (proc.getPkgList().containsKey(packageName)
-                            || (proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) {
-                        procState = proc.setProcState;
+        final int[] procState = {PROCESS_STATE_NONEXISTENT};
+        synchronized (mProcLock) {
+            mProcessList.forEachLruProcessesLOSP(false, proc -> {
+                if (procState[0] > proc.mState.getSetProcState()) {
+                    if (proc.getPkgList().containsKey(packageName) || (proc.getPkgDeps() != null
+                                && proc.getPkgDeps().contains(packageName))) {
+                        procState[0] = proc.mState.getSetProcState();
                     }
                 }
-            }
+            });
         }
-        return procState;
+        return procState[0];
     }
 
     @Override
@@ -2648,11 +2749,12 @@
             throw new SecurityException("Only shell can call it");
         }
         synchronized (this) {
-            final ProcessRecord app = findProcessLocked(process, userId, "setProcessMemoryTrimLevel");
+            final ProcessRecord app = findProcessLOSP(process, userId, "setProcessMemoryTrimLevel");
             if (app == null) {
                 throw new IllegalArgumentException("Unknown process: " + process);
             }
-            if (app.thread == null) {
+            final IApplicationThread thread = app.getThread();
+            if (thread == null) {
                 throw new IllegalArgumentException("Process has no app thread");
             }
             if (app.mProfile.getTrimMemoryLevel() >= level) {
@@ -2660,12 +2762,14 @@
                         "Unable to set a higher trim level than current level");
             }
             if (!(level < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN ||
-                    app.getCurProcState() > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)) {
+                    app.mState.getCurProcState() > PROCESS_STATE_IMPORTANT_FOREGROUND)) {
                 throw new IllegalArgumentException("Unable to set a background trim level "
                     + "on a foreground process");
             }
-            app.thread.scheduleTrimMemory(level);
-            app.mProfile.setTrimMemoryLevel(level);
+            thread.scheduleTrimMemory(level);
+            synchronized (mProcLock) {
+                app.mProfile.setTrimMemoryLevel(level);
+            }
             return true;
         }
     }
@@ -2822,10 +2926,9 @@
      * to the process.
      */
     @GuardedBy("this")
-    final void handleAppDiedLocked(ProcessRecord app,
+    final void handleAppDiedLocked(ProcessRecord app, int pid,
             boolean restarting, boolean allowRestart) {
-        int pid = app.pid;
-        boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
+        boolean kept = cleanUpApplicationRecordLocked(app, pid, restarting, allowRestart, -1,
                 false /*replacingPid*/);
         if (!kept && !restarting) {
             removeLruProcessLocked(app);
@@ -2845,24 +2948,26 @@
         });
     }
 
-    ProcessRecord getRecordForAppLocked(IApplicationThread thread) {
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    ProcessRecord getRecordForAppLOSP(IApplicationThread thread) {
         if (thread == null) {
             return null;
         }
 
-        ProcessRecord record = mProcessList.getLRURecordForAppLocked(thread);
+        ProcessRecord record = mProcessList.getLRURecordForAppLOSP(thread);
         if (record != null) return record;
 
         // Validation: if it isn't in the LRU list, it shouldn't exist, but let's
         // double-check that.
         final IBinder threadBinder = thread.asBinder();
         final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
-                mProcessList.mProcessNames.getMap();
+                mProcessList.getProcessNamesLOSP().getMap();
         for (int i = pmap.size()-1; i >= 0; i--) {
             final SparseArray<ProcessRecord> procs = pmap.valueAt(i);
             for (int j = procs.size()-1; j >= 0; j--) {
                 final ProcessRecord proc = procs.valueAt(j);
-                if (proc.thread != null && proc.thread.asBinder() == threadBinder) {
+                final IApplicationThread procThread = proc.getThread();
+                if (procThread != null && procThread.asBinder() == threadBinder) {
                     Slog.wtf(TAG, "getRecordForApp: exists in name list but not in LRU list: "
                             + proc);
                     return proc;
@@ -2875,7 +2980,7 @@
 
     @GuardedBy("this")
     final void appDiedLocked(ProcessRecord app, String reason) {
-        appDiedLocked(app, app.pid, app.thread, false, reason);
+        appDiedLocked(app, app.getPid(), app.getThread(), false, reason);
     }
 
     @GuardedBy("this")
@@ -2892,26 +2997,31 @@
 
         mBatteryStatsService.noteProcessDied(app.info.uid, pid);
 
-        if (!app.killed) {
+        if (!app.isKilled()) {
             if (!fromBinderDied) {
                 killProcessQuiet(pid);
                 mProcessList.noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
                         ApplicationExitInfo.SUBREASON_UNKNOWN, reason);
             }
             ProcessList.killProcessGroup(app.uid, pid);
-            app.killed = true;
+            synchronized (mProcLock) {
+                app.setKilled(true);
+            }
         }
 
         // Clean up already done if the process has been re-started.
-        if (app.pid == pid && app.thread != null &&
-                app.thread.asBinder() == thread.asBinder()) {
+        IApplicationThread appThread;
+        final int setAdj = app.mState.getSetAdj();
+        final int setProcState = app.mState.getSetProcState();
+        if (app.getPid() == pid && (appThread = app.getThread()) != null
+                && appThread.asBinder() == thread.asBinder()) {
             boolean doLowMem = app.getActiveInstrumentation() == null;
             boolean doOomAdj = doLowMem;
-            if (!app.killedByAm) {
+            if (!app.isKilledByAm()) {
                 reportUidInfoMessageLocked(TAG,
                         "Process " + app.processName + " (pid " + pid + ") has died: "
-                                + ProcessList.makeOomAdjString(app.setAdj, true) + " "
-                                + ProcessList.makeProcStateString(app.setProcState), app.info.uid);
+                        + ProcessList.makeOomAdjString(setAdj, true) + " "
+                        + ProcessList.makeProcStateString(setProcState), app.info.uid);
                 mAppProfiler.setAllowLowerMemLevelLocked(true);
             } else {
                 // Note that we always want to do oom adj to update our state with the
@@ -2919,11 +3029,10 @@
                 mAppProfiler.setAllowLowerMemLevelLocked(false);
                 doLowMem = false;
             }
-            EventLogTags.writeAmProcDied(app.userId, app.pid, app.processName, app.setAdj,
-                    app.setProcState);
+            EventLogTags.writeAmProcDied(app.userId, pid, app.processName, setAdj, setProcState);
             if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                 "Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder());
-            handleAppDiedLocked(app, false, true);
+            handleAppDiedLocked(app, pid, false, true);
 
             if (doOomAdj) {
                 updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
@@ -2931,14 +3040,14 @@
             if (doLowMem) {
                 mAppProfiler.doLowMemReportIfNeededLocked(app);
             }
-        } else if (app.pid != pid) {
+        } else if (app.getPid() != pid) {
             // A new process has already been started.
             reportUidInfoMessageLocked(TAG,
                     "Process " + app.processName + " (pid " + pid
-                            + ") has died and restarted (pid " + app.pid + ").", app.info.uid);
+                            + ") has died and restarted (pid " + app.getPid() + ").", app.info.uid);
 
-            EventLogTags.writeAmProcDied(app.userId, app.pid, app.processName, app.setAdj,
-                    app.setProcState);
+            EventLogTags.writeAmProcDied(app.userId, app.getPid(), app.processName,
+                    setAdj, setProcState);
         } else if (DEBUG_PROCESSES) {
             Slog.d(TAG_PROCESSES, "Received spurious death notification for thread "
                     + thread.asBinder());
@@ -3386,9 +3495,11 @@
                     return;
                 }
                 synchronized (this) {
-                    mProcessList.killPackageProcessesLocked(packageName, appId, targetUserId,
-                            ProcessList.SERVICE_ADJ, ApplicationExitInfo.REASON_USER_REQUESTED,
-                            ApplicationExitInfo.SUBREASON_UNKNOWN, "kill background");
+                    synchronized (mProcLock) {
+                        mProcessList.killPackageProcessesLSP(packageName, appId, targetUserId,
+                                ProcessList.SERVICE_ADJ, ApplicationExitInfo.REASON_USER_REQUESTED,
+                                ApplicationExitInfo.SUBREASON_UNKNOWN, "kill background");
+                    }
                 }
             }
         } finally {
@@ -3413,11 +3524,13 @@
                 // Allow memory level to go down (the flag needs to be set before updating oom adj)
                 // because this method is also used to simulate low memory.
                 mAppProfiler.setAllowLowerMemLevelLocked(true);
-                mProcessList.killPackageProcessesLocked(null /* packageName */, -1 /* appId */,
-                        UserHandle.USER_ALL, ProcessList.CACHED_APP_MIN_ADJ,
-                        ApplicationExitInfo.REASON_USER_REQUESTED,
-                        ApplicationExitInfo.SUBREASON_UNKNOWN,
-                        "kill all background");
+                synchronized (mProcLock) {
+                    mProcessList.killPackageProcessesLSP(null /* packageName */, -1 /* appId */,
+                            UserHandle.USER_ALL, ProcessList.CACHED_APP_MIN_ADJ,
+                            ApplicationExitInfo.REASON_USER_REQUESTED,
+                            ApplicationExitInfo.SUBREASON_UNKNOWN,
+                            "kill all background");
+                }
 
                 mAppProfiler.doLowMemReportIfNeededLocked(null);
             }
@@ -3448,7 +3561,9 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                mProcessList.killAllBackgroundProcessesExceptLocked(minTargetSdk, maxProcState);
+                synchronized (mProcLock) {
+                    mProcessList.killAllBackgroundProcessesExceptLSP(minTargetSdk, maxProcState);
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -3523,11 +3638,14 @@
             proc = mPidsSelfLocked.get(Binder.getCallingPid());
         }
         if (proc != null) {
+            ArraySet<String> pkgDeps = proc.getPkgDeps();
             synchronized (this) {
-                if (proc.pkgDeps == null) {
-                    proc.pkgDeps = new ArraySet<String>(1);
+                synchronized (mProcLock) {
+                    if (pkgDeps == null) {
+                        proc.setPkgDeps(pkgDeps = new ArraySet<String>(1));
+                    }
+                    pkgDeps.add(packageName);
                 }
-                proc.pkgDeps.add(packageName);
             }
         }
     }
@@ -3587,12 +3705,14 @@
         // Check if the caller is actually instrumented and from shell, if it's true, we may lift
         // the throttle of PSS info sampling.
         boolean isCallerInstrumentedFromShell = false;
-        synchronized (mPidsSelfLocked) {
-            ProcessRecord caller = mPidsSelfLocked.get(callingPid);
-            if (caller != null) {
-                final ActiveInstrumentation instr = caller.getActiveInstrumentation();
-                isCallerInstrumentedFromShell = instr != null
-                        && (instr.mSourceUid == SHELL_UID || instr.mSourceUid == ROOT_UID);
+        synchronized (mProcLock) {
+            synchronized (mPidsSelfLocked) {
+                ProcessRecord caller = mPidsSelfLocked.get(callingPid);
+                if (caller != null) {
+                    final ActiveInstrumentation instr = caller.getActiveInstrumentation();
+                    isCallerInstrumentedFromShell = instr != null
+                            && (instr.mSourceUid == SHELL_UID || instr.mSourceUid == ROOT_UID);
+                }
             }
         }
 
@@ -3687,10 +3807,10 @@
         for (int i=pids.length-1; i>=0; i--) {
             ProcessRecord proc;
             int oomAdj;
-            synchronized (this) {
+            synchronized (mProcLock) {
                 synchronized (mPidsSelfLocked) {
                     proc = mPidsSelfLocked.get(pids[i]);
-                    oomAdj = proc != null ? proc.setAdj : 0;
+                    oomAdj = proc != null ? proc.mState.getSetAdj() : 0;
                 }
             }
             if (!allUids || (!allUsers && UserHandle.getUserId(proc.uid) != userId)) {
@@ -3739,9 +3859,10 @@
         if (callerUid == SYSTEM_UID) {
             synchronized (this) {
                 ProcessRecord app = getProcessRecordLocked(processName, uid, true);
-                if (app != null && app.thread != null) {
+                IApplicationThread thread;
+                if (app != null && (thread = app.getThread()) != null) {
                     try {
-                        app.thread.scheduleSuicide();
+                        thread.scheduleSuicide();
                     } catch (RemoteException e) {
                         // If the other end already died, then our work here is done.
                     }
@@ -3903,6 +4024,7 @@
             }
         }
 
+        boolean didSomething;
         if (doit) {
             if (packageName != null) {
                 Slog.i(TAG, "Force stopping " + packageName + " appid=" + appId
@@ -3914,20 +4036,22 @@
             mAppErrors.resetProcessCrashTime(packageName == null, appId, userId);
         }
 
-        // Notify first that the package is stopped, so its process won't be restarted unexpectedly
-        // if there is an activity of the package without attached process becomes visible when
-        // killing its other processes with visible activities.
-        boolean didSomething =
-                mAtmInternal.onForceStopPackage(packageName, doit, evenPersistent, userId);
+        synchronized (mProcLock) {
+            // Notify first that the package is stopped, so its process won't be restarted
+            // unexpectedly if there is an activity of the package without attached process
+            // becomes visible when killing its other processes with visible activities.
+            didSomething = mAtmInternal.onForceStopPackage(
+                    packageName, doit, evenPersistent, userId);
 
-        didSomething |= mProcessList.killPackageProcessesLocked(packageName, appId, userId,
-                ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
-                evenPersistent, true /* setRemoved */,
-                packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
-                        : ApplicationExitInfo.REASON_USER_REQUESTED,
-                ApplicationExitInfo.SUBREASON_UNKNOWN,
-                (packageName == null ? ("stop user " + userId) : ("stop " + packageName))
-                + " due to " + reason);
+            didSomething |= mProcessList.killPackageProcessesLSP(packageName, appId, userId,
+                    ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
+                    evenPersistent, true /* setRemoved */,
+                    packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
+                    : ApplicationExitInfo.REASON_USER_REQUESTED,
+                    ApplicationExitInfo.SUBREASON_UNKNOWN,
+                    (packageName == null ? ("stop user " + userId) : ("stop " + packageName))
+                    + " due to " + reason);
+        }
 
         if (mServices.bringDownDisabledPackageServicesLocked(
                 packageName, null /* filterByClasses */, userId, evenPersistent, doit)) {
@@ -3986,26 +4110,29 @@
 
     @GuardedBy("this")
     private final void processStartTimedOutLocked(ProcessRecord app) {
-        final int pid = app.pid;
-        boolean gone = removePidIfNoThread(app);
+        final int pid = app.getPid();
+        boolean gone = removePidIfNoThreadLocked(app);
 
         if (gone) {
             Slog.w(TAG, "Process " + app + " failed to attach");
             EventLogTags.writeAmProcessStartTimeout(app.userId, pid, app.uid, app.processName);
-            mProcessList.removeProcessNameLocked(app.processName, app.uid);
-            mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
-            mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
-            // Take care of any launching providers waiting for this process.
-            mCpHelper.cleanupAppInLaunchingProvidersLocked(app, true);
-            // Take care of any services that are waiting for the process.
-            mServices.processStartTimedOutLocked(app);
-            app.kill("start timeout", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true);
-            if (app.isolated) {
-                mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+            synchronized (mProcLock) {
+                mProcessList.removeProcessNameLocked(app.processName, app.uid);
+                mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
+                mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
+                // Take care of any launching providers waiting for this process.
+                mCpHelper.cleanupAppInLaunchingProvidersLocked(app, true);
+                // Take care of any services that are waiting for the process.
+                mServices.processStartTimedOutLocked(app);
+                app.killLocked("start timeout", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
+                        true);
+                if (app.isolated) {
+                    mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+                }
+                removeLruProcessLocked(app);
             }
-            removeLruProcessLocked(app);
             final BackupRecord backupTarget = mBackupTargets.get(app.userId);
-            if (backupTarget != null && backupTarget.app.pid == pid) {
+            if (backupTarget != null && backupTarget.app.getPid() == pid) {
                 Slog.w(TAG, "Unattached app died before backup, skipping");
                 mHandler.post(new Runnable() {
                 @Override
@@ -4043,7 +4170,7 @@
             synchronized (mPidsSelfLocked) {
                 app = mPidsSelfLocked.get(pid);
             }
-            if (app != null && (app.startUid != callingUid || app.startSeq != startSeq)) {
+            if (app != null && (app.getStartUid() != callingUid || app.getStartSeq() != startSeq)) {
                 String processName = null;
                 final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
                 if (pending != null) {
@@ -4053,14 +4180,14 @@
                         + " startSeq:" + startSeq
                         + " pid:" + pid
                         + " belongs to another existing app:" + app.processName
-                        + " startSeq:" + app.startSeq;
+                        + " startSeq:" + app.getStartSeq();
                 Slog.wtf(TAG, msg);
                 // SafetyNet logging for b/131105245.
-                EventLog.writeEvent(0x534e4554, "131105245", app.startUid, msg);
+                EventLog.writeEvent(0x534e4554, "131105245", app.getStartUid(), msg);
                 // If there is already an app occupying that pid that hasn't been cleaned up
-                cleanUpApplicationRecordLocked(app, false, false, -1,
-                            true /*replacingPid*/);
-                removePidLocked(app);
+                cleanUpApplicationRecordLocked(app, pid, false, false, -1,
+                        true /*replacingPid*/);
+                removePidLocked(pid, app);
                 app = null;
             }
         } else {
@@ -4071,10 +4198,10 @@
         // update the internal state.
         if (app == null && startSeq > 0) {
             final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
-            if (pending != null && pending.startUid == callingUid && pending.startSeq == startSeq
-                    && mProcessList.handleProcessStartedLocked(pending, pid, pending
-                            .isUsingWrapper(),
-                            startSeq, true)) {
+            if (pending != null && pending.getStartUid() == callingUid
+                    && pending.getStartSeq() == startSeq
+                    && mProcessList.handleProcessStartedLocked(pending, pid,
+                        pending.isUsingWrapper(), startSeq, true)) {
                 app = pending;
             }
         }
@@ -4100,8 +4227,8 @@
 
         // If this application record is still attached to a previous
         // process, clean it up now.
-        if (app.thread != null) {
-            handleAppDiedLocked(app, true, true);
+        if (app.getThread() != null) {
+            handleAppDiedLocked(app, pid, true, true);
         }
 
         // Tell the process all about itself.
@@ -4114,7 +4241,7 @@
             AppDeathRecipient adr = new AppDeathRecipient(
                     app, pid, thread);
             thread.asBinder().linkToDeath(adr, 0);
-            app.deathRecipient = adr;
+            app.setDeathRecipient(adr);
         } catch (RemoteException e) {
             app.resetPackageList(mProcessStats);
             mProcessList.startProcessLocked(app,
@@ -4123,23 +4250,25 @@
             return false;
         }
 
-        EventLogTags.writeAmProcBound(app.userId, app.pid, app.processName);
+        EventLogTags.writeAmProcBound(app.userId, pid, app.processName);
 
-        app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
-        mOomAdjuster.setAttachingSchedGroupLocked(app);
-        app.forcingToImportant = null;
-        updateProcessForegroundLocked(app, false, 0, false);
-        app.hasShownUi = false;
-        app.setDebugging(false);
-        app.setCached(false);
-        app.killedByAm = false;
-        app.killed = false;
-
-
-        // We carefully use the same state that PackageManager uses for
-        // filtering, since we use this flag to decide if we need to install
-        // providers when user is unlocked later
-        app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);
+        synchronized (mProcLock) {
+            app.mState.setCurAdj(ProcessList.INVALID_ADJ);
+            app.mState.setSetAdj(ProcessList.INVALID_ADJ);
+            app.mState.setVerifiedAdj(ProcessList.INVALID_ADJ);
+            mOomAdjuster.setAttachingSchedGroupLSP(app);
+            app.mState.setForcingToImportant(null);
+            updateProcessForegroundLocked(app, false, 0, false);
+            app.mState.setHasShownUi(false);
+            app.mState.setCached(false);
+            app.setDebugging(false);
+            app.setKilledByAm(false);
+            app.setKilled(false);
+            // We carefully use the same state that PackageManager uses for
+            // filtering, since we use this flag to decide if we need to install
+            // providers when user is unlocked later
+            app.setUnlocked(StorageManager.isUserKeyUnlocked(app.userId));
+        }
 
         mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
 
@@ -4179,9 +4308,11 @@
             }
 
             boolean enableTrackAllocation = false;
-            if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
-                enableTrackAllocation = true;
-                mTrackAllocationApp = null;
+            synchronized (mProcLock) {
+                if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
+                    enableTrackAllocation = true;
+                    mTrackAllocationApp = null;
+                }
             }
 
             // If the app is being launched for restore or full backup, set it up specially
@@ -4202,7 +4333,7 @@
             ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s",
                     processName, app.getWindowProcessController().getConfiguration());
             ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
-            app.compat = compatibilityInfoForPackage(appInfo);
+            app.setCompat(compatibilityInfoForPackage(appInfo));
 
             ProfilerInfo profilerInfo = mAppProfiler.setupProfilerInfoLocked(thread, app, instr);
 
@@ -4246,10 +4377,11 @@
                 mPlatformCompat.resetReporting(app.info);
             }
             final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
-            if (app.isolatedEntryPoint != null) {
+            if (app.getIsolatedEntryPoint() != null) {
                 // This is an isolated process which should just call an entry point instead of
                 // being bound to an application.
-                thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
+                thread.runIsolatedEntryPoint(
+                        app.getIsolatedEntryPoint(), app.getIsolatedEntryPointArgs());
             } else if (instr2 != null) {
                 thread.bindApplication(processName, appInfo, providerList,
                         instr2.mClass,
@@ -4259,20 +4391,20 @@
                         mBinderTransactionTrackingEnabled, enableTrackAllocation,
                         isRestrictedBackupMode || !normalMode, app.isPersistent(),
                         new Configuration(app.getWindowProcessController().getConfiguration()),
-                        app.compat, getCommonServicesLocked(app.isolated),
+                        app.getCompat(), getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
                         buildSerial, autofillOptions, contentCaptureOptions,
-                        app.mDisabledCompatChanges, serializedSystemFontMap);
+                        app.getDisabledCompatChanges(), serializedSystemFontMap);
             } else {
                 thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
                         null, null, null, testMode,
                         mBinderTransactionTrackingEnabled, enableTrackAllocation,
                         isRestrictedBackupMode || !normalMode, app.isPersistent(),
                         new Configuration(app.getWindowProcessController().getConfiguration()),
-                        app.compat, getCommonServicesLocked(app.isolated),
+                        app.getCompat(), getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
                         buildSerial, autofillOptions, contentCaptureOptions,
-                        app.mDisabledCompatChanges, serializedSystemFontMap);
+                        app.getDisabledCompatChanges(), serializedSystemFontMap);
             }
             if (profilerInfo != null) {
                 profilerInfo.closeFd();
@@ -4281,9 +4413,11 @@
 
             // Make app active after binding application or client may be running requests (e.g
             // starting activities) before it is ready.
-            app.makeActive(thread, mProcessStats);
-            checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
-            mProcessList.updateLruProcessLocked(app, false, null);
+            synchronized (mProcLock) {
+                app.makeActive(thread, mProcessStats);
+                checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
+            }
+            updateLruProcessLocked(app, false, null);
             checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
             final long now = SystemClock.uptimeMillis();
             synchronized (mAppProfiler.mProfilerLock) {
@@ -4295,8 +4429,9 @@
             Slog.wtf(TAG, "Exception thrown during bind of " + app, e);
             app.resetPackageList(mProcessStats);
             app.unlinkDeathRecipient();
-            app.kill("error during bind", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true);
-            handleAppDiedLocked(app, false, true);
+            app.killLocked("error during bind", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
+                    true);
+            handleAppDiedLocked(app, pid, false, true);
             return false;
         }
 
@@ -4359,8 +4494,9 @@
         }
 
         if (badApp) {
-            app.kill("error during init", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true);
-            handleAppDiedLocked(app, false, true);
+            app.killLocked("error during init", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
+                    true);
+            handleAppDiedLocked(app, pid, false, true);
             return false;
         }
 
@@ -4372,14 +4508,14 @@
         FrameworkStatsLog.write(
                 FrameworkStatsLog.PROCESS_START_TIME,
                 app.info.uid,
-                app.pid,
+                pid,
                 app.info.packageName,
                 FrameworkStatsLog.PROCESS_START_TIME__TYPE__COLD,
-                app.startTime,
-                (int) (bindApplicationTimeMillis - app.startTime),
-                (int) (SystemClock.elapsedRealtime() - app.startTime),
-                app.hostingRecord.getType(),
-                (app.hostingRecord.getName() != null ? app.hostingRecord.getName() : ""));
+                app.getStartTime(),
+                (int) (bindApplicationTimeMillis - app.getStartTime()),
+                (int) (SystemClock.elapsedRealtime() - app.getStartTime()),
+                app.getHostingRecord().getType(),
+                (app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : ""));
         return true;
     }
 
@@ -4472,11 +4608,11 @@
             // up.
             final int NP = mProcessesOnHold.size();
             if (NP > 0) {
-                ArrayList<ProcessRecord> procs =
-                    new ArrayList<ProcessRecord>(mProcessesOnHold);
-                for (int ip=0; ip<NP; ip++) {
-                    if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "Starting process on hold: "
-                            + procs.get(ip));
+                ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(mProcessesOnHold);
+                for (int ip = 0; ip < NP; ip++) {
+                    if (DEBUG_PROCESSES) {
+                        Slog.v(TAG_PROCESSES, "Starting process on hold: " + procs.get(ip));
+                    }
                     mProcessList.startProcessLocked(procs.get(ip),
                             new HostingRecord("on-hold"),
                             ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);
@@ -4508,8 +4644,8 @@
                         public void performReceive(Intent intent, int resultCode,
                                 String data, Bundle extras, boolean ordered,
                                 boolean sticky, int sendingUser) {
-                            synchronized (ActivityManagerService.this) {
-                                mAppProfiler.requestPssAllProcsLocked(
+                            synchronized (mProcLock) {
+                                mAppProfiler.requestPssAllProcsLPr(
                                         SystemClock.uptimeMillis(), true, false);
                             }
                         }
@@ -4944,7 +5080,7 @@
                 if (pr == null) {
                     return;
                 }
-                pr.forcingToImportant = null;
+                pr.mState.setForcingToImportant(null);
                 updateProcessForegroundLocked(pr, false, 0, false);
             }
             updateOomAdjLocked(pr, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
@@ -4970,7 +5106,7 @@
                     oldToken.token.unlinkToDeath(oldToken, 0);
                     mImportantProcesses.remove(pid);
                     if (pr != null) {
-                        pr.forcingToImportant = null;
+                        pr.mState.setForcingToImportant(null);
                     }
                     changed = true;
                 }
@@ -4984,7 +5120,7 @@
                     try {
                         token.linkToDeath(newToken, 0);
                         mImportantProcesses.put(pid, newToken);
-                        pr.forcingToImportant = newToken;
+                        pr.mState.setForcingToImportant(newToken);
                         changed = true;
                     } catch (RemoteException e) {
                         // If the process died while doing this, we will later
@@ -5000,13 +5136,12 @@
     }
 
     private boolean isAppForeground(int uid) {
-        synchronized (this) {
+        synchronized (mProcLock) {
             UidRecord uidRec = mProcessList.mActiveUids.get(uid);
-            if (uidRec == null || uidRec.idle) {
+            if (uidRec == null || uidRec.isIdle()) {
                 return false;
             }
-            return uidRec.getCurProcState()
-                    <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+            return uidRec.getCurProcState() <= PROCESS_STATE_IMPORTANT_FOREGROUND;
         }
     }
 
@@ -5017,11 +5152,16 @@
     // NOTE: this is an internal method used by the OnShellCommand implementation only and should
     // be guarded by permission checking.
     int getUidState(int uid) {
-        synchronized (this) {
-            return mProcessList.getUidProcStateLocked(uid);
+        synchronized (mProcLock) {
+            return mProcessList.getUidProcStateLOSP(uid);
         }
     }
 
+    @GuardedBy("this")
+    int getUidStateLocked(int uid) {
+        return mProcessList.getUidProcStateLOSP(uid);
+    }
+
     // =========================================================
     // PROCESS INFO
     // =========================================================
@@ -5070,20 +5210,23 @@
             throw new IllegalArgumentException("pids and scores arrays have different lengths!");
         }
 
-        synchronized (mPidsSelfLocked) {
-            for (int i = 0; i < pids.length; i++) {
-                ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
-                if (pr != null) {
-                    final boolean isPendingTop =
+        synchronized (mProcLock) {
+            synchronized (mPidsSelfLocked) {
+                for (int i = 0; i < pids.length; i++) {
+                    ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
+                    if (pr != null) {
+                        final boolean isPendingTop =
                                 mPendingStartActivityUids.isPendingTopPid(pr.uid, pids[i]);
-                    states[i] = isPendingTop ? PROCESS_STATE_TOP : pr.getCurProcState();
-                    if (scores != null) {
-                        scores[i] = isPendingTop ? (ProcessList.FOREGROUND_APP_ADJ - 1) : pr.curAdj;
-                    }
-                } else {
-                    states[i] = PROCESS_STATE_NONEXISTENT;
-                    if (scores != null) {
-                        scores[i] = ProcessList.INVALID_ADJ;
+                        states[i] = isPendingTop ? PROCESS_STATE_TOP : pr.mState.getCurProcState();
+                        if (scores != null) {
+                            scores[i] = isPendingTop
+                                    ? (ProcessList.FOREGROUND_APP_ADJ - 1) : pr.mState.getCurAdj();
+                        }
+                    } else {
+                        states[i] = PROCESS_STATE_NONEXISTENT;
+                        if (scores != null) {
+                            scores[i] = ProcessList.INVALID_ADJ;
+                        }
                     }
                 }
             }
@@ -5261,8 +5404,8 @@
     }
 
     public boolean isAppStartModeDisabled(int uid, String packageName) {
-        synchronized (this) {
-            return getAppStartModeLocked(uid, packageName, 0, -1, false, true, false)
+        synchronized (mProcLock) {
+            return getAppStartModeLOSP(uid, packageName, 0, -1, false, true, false)
                     == ActivityManager.APP_START_MODE_DISABLED;
         }
     }
@@ -5273,7 +5416,8 @@
     }
 
     // Unified app-op and target sdk check
-    int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    int appRestrictedInBackgroundLOSP(int uid, String packageName, int packageTargetSdk) {
         // Apps that target O+ are always subject to background check
         if (packageTargetSdk >= Build.VERSION_CODES.O) {
             if (DEBUG_BACKGROUND_CHECK) {
@@ -5302,7 +5446,7 @@
                 // If force-background-check is enabled, restrict all apps that aren't whitelisted.
                 if (mForceBackgroundCheck &&
                         !UserHandle.isCore(uid) &&
-                        !isOnDeviceIdleAllowlistLocked(uid, /*allowExceptIdleToo=*/ true)) {
+                        !isOnDeviceIdleAllowlistLOSP(uid, /*allowExceptIdleToo=*/ true)) {
                     if (DEBUG_BACKGROUND_CHECK) {
                         Slog.i(TAG, "Force background check: " +
                                 uid + "/" + packageName + " restricted");
@@ -5320,7 +5464,8 @@
     // Service launch is available to apps with run-in-background exemptions but
     // some other background operations are not.  If we're doing a check
     // of service-launch policy, allow those callers to proceed unrestricted.
-    int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    int appServicesRestrictedInBackgroundLOSP(int uid, String packageName, int packageTargetSdk) {
         // Persistent app?
         if (mPackageManagerInt.isPackagePersistent(packageName)) {
             if (DEBUG_BACKGROUND_CHECK) {
@@ -5331,7 +5476,7 @@
         }
 
         // Non-persistent but background whitelisted?
-        if (uidOnBackgroundAllowlist(uid)) {
+        if (uidOnBackgroundAllowlistLOSP(uid)) {
             if (DEBUG_BACKGROUND_CHECK) {
                 Slog.i(TAG, "App " + uid + "/" + packageName
                         + " on background whitelist; not restricted in background");
@@ -5340,7 +5485,7 @@
         }
 
         // Is this app on the battery whitelist?
-        if (isOnDeviceIdleAllowlistLocked(uid, /*allowExceptIdleToo=*/ false)) {
+        if (isOnDeviceIdleAllowlistLOSP(uid, /*allowExceptIdleToo=*/ false)) {
             if (DEBUG_BACKGROUND_CHECK) {
                 Slog.i(TAG, "App " + uid + "/" + packageName
                         + " on idle whitelist; not restricted in background");
@@ -5349,25 +5494,26 @@
         }
 
         // None of the service-policy criteria apply, so we apply the common criteria
-        return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
+        return appRestrictedInBackgroundLOSP(uid, packageName, packageTargetSdk);
     }
 
-    int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    int getAppStartModeLOSP(int uid, String packageName, int packageTargetSdk,
             int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
         if (mInternal.isPendingTopUid(uid)) {
             return ActivityManager.APP_START_MODE_NORMAL;
         }
-        UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
+        UidRecord uidRec = mProcessList.getUidRecordLOSP(uid);
         if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
                 + packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
-                + (uidRec != null ? uidRec.idle : false));
-        if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.idle) {
+                + (uidRec != null ? uidRec.isIdle() : false));
+        if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.isIdle()) {
             boolean ephemeral;
             if (uidRec == null) {
                 ephemeral = getPackageManagerInternal().isPackageEphemeral(
                         UserHandle.getUserId(uid), packageName);
             } else {
-                ephemeral = uidRec.ephemeral;
+                ephemeral = uidRec.isEphemeral();
             }
 
             if (ephemeral) {
@@ -5382,14 +5528,14 @@
                     return ActivityManager.APP_START_MODE_NORMAL;
                 }
                 final int startMode = (alwaysRestrict)
-                        ? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
-                        : appServicesRestrictedInBackgroundLocked(uid, packageName,
+                        ? appRestrictedInBackgroundLOSP(uid, packageName, packageTargetSdk)
+                        : appServicesRestrictedInBackgroundLOSP(uid, packageName,
                                 packageTargetSdk);
                 if (DEBUG_BACKGROUND_CHECK) {
                     Slog.d(TAG, "checkAllowBackground: uid=" + uid
                             + " pkg=" + packageName + " startMode=" + startMode
-                            + " onallowlist=" + isOnDeviceIdleAllowlistLocked(uid, false)
-                            + " onallowlist(ei)=" + isOnDeviceIdleAllowlistLocked(uid, true));
+                            + " onallowlist=" + isOnDeviceIdleAllowlistLOSP(uid, false)
+                            + " onallowlist(ei)=" + isOnDeviceIdleAllowlistLOSP(uid, true));
                 }
                 if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
                     // This is an old app that has been forced into a "compatible as possible"
@@ -5400,8 +5546,8 @@
                         synchronized (mPidsSelfLocked) {
                             proc = mPidsSelfLocked.get(callingPid);
                         }
-                        if (proc != null &&
-                                !ActivityManager.isProcStateBackground(proc.getCurProcState())) {
+                        if (proc != null && !ActivityManager.isProcStateBackground(
+                                proc.mState.getCurProcState())) {
                             // Whoever is instigating this is in the foreground, so we will allow it
                             // to go through.
                             return ActivityManager.APP_START_MODE_NORMAL;
@@ -5417,7 +5563,8 @@
     /**
      * @return whether a UID is in the system, user or temp doze allowlist.
      */
-    boolean isOnDeviceIdleAllowlistLocked(int uid, boolean allowExceptIdleToo) {
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    boolean isOnDeviceIdleAllowlistLOSP(int uid, boolean allowExceptIdleToo) {
         final int appId = UserHandle.getAppId(uid);
 
         final int[] allowlist = allowExceptIdleToo
@@ -5429,7 +5576,8 @@
                 || mPendingTempAllowlist.indexOfKey(uid) >= 0;
     }
 
-    boolean isAllowlistedForFgsStartLocked(int uid) {
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    boolean isAllowlistedForFgsStartLOSP(int uid) {
         return Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, UserHandle.getAppId(uid)) >= 0
                 || mFgsStartTempAllowList.isAllowed(uid);
     }
@@ -5438,7 +5586,8 @@
      * @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on
      * the allowlist
      */
-    String getPendingTempAllowlistTagForUidLocked(int uid) {
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    String getPendingTempAllowlistTagForUidLOSP(int uid) {
         final PendingTempAllowlist ptw = mPendingTempAllowlist.get(uid);
         return ptw != null ? ptw.tag : null;
     }
@@ -5483,8 +5632,8 @@
             final int modeFlags, int userId) {
         enforceNotIsolatedCaller("grantUriPermission");
         GrantUri grantUri = new GrantUri(userId, uri, modeFlags);
-        synchronized(this) {
-            final ProcessRecord r = getRecordForAppLocked(caller);
+        synchronized (this) {
+            final ProcessRecord r = getRecordForAppLOSP(caller);
             if (r == null) {
                 throw new SecurityException("Unable to find app for caller "
                         + caller
@@ -5517,8 +5666,8 @@
     public void revokeUriPermission(IApplicationThread caller, String targetPackage, Uri uri,
             final int modeFlags, int userId) {
         enforceNotIsolatedCaller("revokeUriPermission");
-        synchronized(this) {
-            final ProcessRecord r = getRecordForAppLocked(caller);
+        synchronized (this) {
+            final ProcessRecord r = getRecordForAppLOSP(caller);
             if (r == null) {
                 throw new SecurityException("Unable to find app for caller "
                         + caller
@@ -5549,9 +5698,8 @@
 
     @Override
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
-        synchronized (this) {
-            ProcessRecord app =
-                who != null ? getRecordForAppLocked(who) : null;
+        synchronized (mProcLock) {
+            final ProcessRecord app = who != null ? getRecordForAppLOSP(who) : null;
             if (app == null) return;
 
             Message msg = Message.obtain();
@@ -5850,7 +5998,8 @@
     // GLOBAL MANAGEMENT
     // =========================================================
 
-    private boolean uidOnBackgroundAllowlist(final int uid) {
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    private boolean uidOnBackgroundAllowlistLOSP(final int uid) {
         final int appId = UserHandle.getAppId(uid);
         final int[] allowlist = mBackgroundAppIdAllowlist;
         for (int i = 0, len = allowlist.length; i < len; i++) {
@@ -5894,11 +6043,13 @@
             Slog.i(TAG, "Adding uid " + uid + " to bg uid whitelist");
         }
         synchronized (this) {
-            final int num = mBackgroundAppIdAllowlist.length;
-            int[] newList = new int[num + 1];
-            System.arraycopy(mBackgroundAppIdAllowlist, 0, newList, 0, num);
-            newList[num] = UserHandle.getAppId(uid);
-            mBackgroundAppIdAllowlist = newList;
+            synchronized (mProcLock) {
+                final int num = mBackgroundAppIdAllowlist.length;
+                int[] newList = new int[num + 1];
+                System.arraycopy(mBackgroundAppIdAllowlist, 0, newList, 0, num);
+                newList[num] = UserHandle.getAppId(uid);
+                mBackgroundAppIdAllowlist = newList;
+            }
         }
     }
 
@@ -5924,8 +6075,8 @@
         if (app == null) {
             app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0,
                     new HostingRecord("added application",
-                            customProcess != null ? customProcess : info.processName));
-            mProcessList.updateLruProcessLocked(app, false, null);
+                        customProcess != null ? customProcess : info.processName));
+            updateLruProcessLocked(app, false, null);
             updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
         }
 
@@ -5941,9 +6092,9 @@
 
         if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
             app.setPersistent(true);
-            app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
+            app.mState.setMaxAdj(ProcessList.PERSISTENT_PROC_ADJ);
         }
-        if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
+        if (app.getThread() == null && mPersistentStartingProcesses.indexOf(app) < 0) {
             mPersistentStartingProcesses.add(app);
             mProcessList.startProcessLocked(app, new HostingRecord("added application",
                     customProcess != null ? customProcess : app.processName),
@@ -6014,7 +6165,7 @@
     }
 
     void onWakefulnessChanged(int wakefulness) {
-        synchronized(this) {
+        synchronized (this) {
             boolean wasAwake = mWakefulness.getAndSet(wakefulness)
                     == PowerManagerInternal.WAKEFULNESS_AWAKE;
             boolean isAwake = wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
@@ -6141,13 +6292,13 @@
     }
 
     void setTrackAllocationApp(ApplicationInfo app, String processName) {
-        synchronized (this) {
-            if (!Build.IS_DEBUGGABLE) {
-                if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
-                    throw new SecurityException("Process not debuggable: " + app.packageName);
-                }
+        if (!Build.IS_DEBUGGABLE) {
+            if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+                throw new SecurityException("Process not debuggable: " + app.packageName);
             }
+        }
 
+        synchronized (mProcLock) {
             mTrackAllocationApp = processName;
         }
     }
@@ -6204,7 +6355,7 @@
 
     @Override
     public void setUserIsMonkey(boolean userIsMonkey) {
-        synchronized (this) {
+        synchronized (mProcLock) {
             synchronized (mPidsSelfLocked) {
                 final int callingPid = Binder.getCallingPid();
                 ProcessRecord proc = mPidsSelfLocked.get(callingPid);
@@ -6223,7 +6374,7 @@
 
     @Override
     public boolean isUserAMonkey() {
-        synchronized (this) {
+        synchronized (mProcLock) {
             // If there is a controller also implies the user is a monkey.
             return mUserIsMonkey || mActivityTaskManager.isControllerAMonkey();
         }
@@ -6444,8 +6595,8 @@
                     "getUidProcessState");
         }
 
-        synchronized (this) {
-            return mProcessList.getUidProcStateLocked(uid);
+        synchronized (mProcLock) {
+            return mProcessList.getUidProcStateLOSP(uid);
         }
     }
 
@@ -6471,17 +6622,18 @@
             enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
                     "isUidActive");
         }
-        synchronized (this) {
-            if (isUidActiveLocked(uid)) {
+        synchronized (mProcLock) {
+            if (isUidActiveLOSP(uid)) {
                 return true;
             }
         }
         return mInternal.isPendingTopUid(uid);
     }
 
-    boolean isUidActiveLocked(int uid) {
-        final UidRecord uidRecord = mProcessList.getUidRecordLocked(uid);
-        return uidRecord != null && !uidRecord.setIdle;
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    boolean isUidActiveLOSP(int uid) {
+        final UidRecord uidRecord = mProcessList.getUidRecordLOSP(uid);
+        return uidRecord != null && !uidRecord.isSetIdle();
     }
 
     @Override
@@ -6539,7 +6691,7 @@
 
     @Override
     public void setRenderThread(int tid) {
-        synchronized (this) {
+        synchronized (mProcLock) {
             ProcessRecord proc;
             int pid = Binder.getCallingPid();
             if (pid == Process.myPid()) {
@@ -6548,32 +6700,31 @@
             }
             synchronized (mPidsSelfLocked) {
                 proc = mPidsSelfLocked.get(pid);
-                if (proc != null && proc.renderThreadTid == 0 && tid > 0) {
-                    // ensure the tid belongs to the process
-                    if (!isThreadInProcess(pid, tid)) {
-                        throw new IllegalArgumentException(
+            }
+            if (proc != null && proc.getRenderThreadTid() == 0 && tid > 0) {
+                // ensure the tid belongs to the process
+                if (!isThreadInProcess(pid, tid)) {
+                    throw new IllegalArgumentException(
                             "Render thread does not belong to process");
-                    }
-                    proc.renderThreadTid = tid;
-                    if (DEBUG_OOM_ADJ) {
-                        Slog.d("UI_FIFO", "Set RenderThread tid " + tid + " for pid " + pid);
-                    }
-                    // promote to FIFO now
-                    if (proc.getCurrentSchedulingGroup() == ProcessList.SCHED_GROUP_TOP_APP) {
-                        if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
-                        if (mUseFifoUiScheduling) {
-                            setThreadScheduler(proc.renderThreadTid,
+                }
+                proc.setRenderThreadTid(tid);
+                if (DEBUG_OOM_ADJ) {
+                    Slog.d("UI_FIFO", "Set RenderThread tid " + tid + " for pid " + pid);
+                }
+                // promote to FIFO now
+                if (proc.mState.getCurrentSchedulingGroup() == ProcessList.SCHED_GROUP_TOP_APP) {
+                    if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
+                    if (mUseFifoUiScheduling) {
+                        setThreadScheduler(proc.getRenderThreadTid(),
                                 SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
-                        } else {
-                            setThreadPriority(proc.renderThreadTid, TOP_APP_PRIORITY_BOOST);
-                        }
+                    } else {
+                        setThreadPriority(proc.getRenderThreadTid(), TOP_APP_PRIORITY_BOOST);
                     }
-                } else {
-                    if (DEBUG_OOM_ADJ) {
-                        Slog.d("UI_FIFO", "Didn't set thread from setRenderThread? " +
-                               "PID: " + pid + ", TID: " + tid + " FIFO: " +
-                               mUseFifoUiScheduling);
-                    }
+                }
+            } else {
+                if (DEBUG_OOM_ADJ) {
+                    Slog.d("UI_FIFO", "Didn't set thread from setRenderThread? "
+                            + "PID: " + pid + ", TID: " + tid + " FIFO: " + mUseFifoUiScheduling);
                 }
             }
         }
@@ -6630,11 +6781,11 @@
                         Slog.w(TAG, "setHasTopUi called on unknown pid: " + pid);
                         return;
                     }
-                    if (pr.hasTopUi() != hasTopUi) {
+                    if (pr.mState.hasTopUi() != hasTopUi) {
                         if (DEBUG_OOM_ADJ) {
                             Slog.d(TAG, "Setting hasTopUi=" + hasTopUi + " for pid=" + pid);
                         }
-                        pr.setHasTopUi(hasTopUi);
+                        pr.mState.setHasTopUi(hasTopUi);
                         changed = true;
                     }
                 }
@@ -6814,42 +6965,46 @@
         // manager calls in with its locks held.
 
         boolean killed = false;
-        synchronized (mPidsSelfLocked) {
-            int worstType = 0;
-            for (int i=0; i<pids.length; i++) {
-                ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
-                if (proc != null) {
-                    int type = proc.setAdj;
-                    if (type > worstType) {
-                        worstType = type;
+        synchronized (this) {
+            synchronized (mProcLock) {
+                synchronized (mPidsSelfLocked) {
+                    int worstType = 0;
+                    for (int i = 0; i < pids.length; i++) {
+                        ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
+                        if (proc != null) {
+                            int type = proc.mState.getSetAdj();
+                            if (type > worstType) {
+                                worstType = type;
+                            }
+                        }
                     }
-                }
-            }
 
-            // If the worst oom_adj is somewhere in the cached proc LRU range,
-            // then constrain it so we will kill all cached procs.
-            if (worstType < ProcessList.CACHED_APP_MAX_ADJ
-                    && worstType > ProcessList.CACHED_APP_MIN_ADJ) {
-                worstType = ProcessList.CACHED_APP_MIN_ADJ;
-            }
+                    // If the worst oom_adj is somewhere in the cached proc LRU range,
+                    // then constrain it so we will kill all cached procs.
+                    if (worstType < ProcessList.CACHED_APP_MAX_ADJ
+                            && worstType > ProcessList.CACHED_APP_MIN_ADJ) {
+                        worstType = ProcessList.CACHED_APP_MIN_ADJ;
+                    }
 
-            // If this is not a secure call, don't let it kill processes that
-            // are important.
-            if (!secure && worstType < ProcessList.SERVICE_ADJ) {
-                worstType = ProcessList.SERVICE_ADJ;
-            }
+                    // If this is not a secure call, don't let it kill processes that
+                    // are important.
+                    if (!secure && worstType < ProcessList.SERVICE_ADJ) {
+                        worstType = ProcessList.SERVICE_ADJ;
+                    }
 
-            Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType);
-            for (int i=0; i<pids.length; i++) {
-                ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
-                if (proc == null) {
-                    continue;
-                }
-                int adj = proc.setAdj;
-                if (adj >= worstType && !proc.killedByAm) {
-                    proc.kill(reason, ApplicationExitInfo.REASON_OTHER,
-                            ApplicationExitInfo.SUBREASON_KILL_PID, true);
-                    killed = true;
+                    Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType);
+                    for (int i = 0; i < pids.length; i++) {
+                        ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
+                        if (proc == null) {
+                            continue;
+                        }
+                        int adj = proc.mState.getSetAdj();
+                        if (adj >= worstType && !proc.isKilledByAm()) {
+                            proc.killLocked(reason, ApplicationExitInfo.REASON_OTHER,
+                                    ApplicationExitInfo.SUBREASON_KILL_PID, true);
+                            killed = true;
+                        }
+                    }
                 }
             }
         }
@@ -6862,13 +7017,15 @@
         synchronized (this) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                mProcessList.killPackageProcessesLocked(null /* packageName */, appId, userId,
-                        ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
-                        true /* callerWillRestart */, true /* doit */, true /* evenPersistent */,
-                        false /* setRemoved */,
-                        ApplicationExitInfo.REASON_OTHER,
-                        ApplicationExitInfo.SUBREASON_KILL_UID,
-                        reason != null ? reason : "kill uid");
+                synchronized (mProcLock) {
+                    mProcessList.killPackageProcessesLSP(null /* packageName */, appId, userId,
+                            ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
+                            true /* callerWillRestart */, true /* doit */,
+                            true /* evenPersistent */, false /* setRemoved */,
+                            ApplicationExitInfo.REASON_OTHER,
+                            ApplicationExitInfo.SUBREASON_KILL_UID,
+                            reason != null ? reason : "kill uid");
+                }
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -6881,13 +7038,15 @@
         synchronized (this) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                mProcessList.killPackageProcessesLocked(null /* packageName */, appId, userId,
-                        ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
-                        true /* callerWillRestart */, true /* doit */, true /* evenPersistent */,
-                        false /* setRemoved */,
-                        ApplicationExitInfo.REASON_PERMISSION_CHANGE,
-                        ApplicationExitInfo.SUBREASON_UNKNOWN,
-                        reason != null ? reason : "kill uid");
+                synchronized (mProcLock) {
+                    mProcessList.killPackageProcessesLSP(null /* packageName */, appId, userId,
+                            ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
+                            true /* callerWillRestart */, true /* doit */,
+                            true /* evenPersistent */, false /* setRemoved */,
+                            ApplicationExitInfo.REASON_PERMISSION_CHANGE,
+                            ApplicationExitInfo.SUBREASON_UNKNOWN,
+                            reason != null ? reason : "kill uid");
+                }
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -6909,17 +7068,22 @@
         }
 
         boolean killed = false;
-        synchronized (mPidsSelfLocked) {
-            final int size = mPidsSelfLocked.size();
-            for (int i = 0; i < size; i++) {
-                final int pid = mPidsSelfLocked.keyAt(i);
-                final ProcessRecord proc = mPidsSelfLocked.valueAt(i);
-                if (proc == null) continue;
+        synchronized (this) {
+            synchronized (mProcLock) {
+                synchronized (mPidsSelfLocked) {
+                    final int size = mPidsSelfLocked.size();
+                    for (int i = 0; i < size; i++) {
+                        final int pid = mPidsSelfLocked.keyAt(i);
+                        final ProcessRecord proc = mPidsSelfLocked.valueAt(i);
+                        if (proc == null) continue;
 
-                final int adj = proc.setAdj;
-                if (adj > belowAdj && !proc.killedByAm) {
-                    proc.kill(reason, ApplicationExitInfo.REASON_PERMISSION_CHANGE, true);
-                    killed = true;
+                        final int adj = proc.mState.getSetAdj();
+                        if (adj > belowAdj && !proc.isKilledByAm()) {
+                            proc.killLocked(reason, ApplicationExitInfo.REASON_PERMISSION_CHANGE,
+                                    true);
+                            killed = true;
+                        }
+                    }
                 }
             }
         }
@@ -7025,16 +7189,16 @@
                     + android.Manifest.permission.SET_ACTIVITY_WATCHER);
         }
 
-        synchronized (this) {
+        synchronized (mProcLock) {
             final long now = SystemClock.uptimeMillis();
             final long timeSinceLastIdle = now - mLastIdleTime;
 
             // Compact all non-zygote processes to freshen up the page cache.
             mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
 
-            final long lowRamSinceLastIdle = mAppProfiler.getLowRamTimeSinceIdleLocked(now);
+            final long lowRamSinceLastIdle = mAppProfiler.getLowRamTimeSinceIdleLPr(now);
             mLastIdleTime = now;
-            mAppProfiler.updateLowRamTimestampLocked(now);
+            mAppProfiler.updateLowRamTimestampLPr(now);
 
             StringBuilder sb = new StringBuilder(128);
             sb.append("Idle maintenance over ");
@@ -7051,13 +7215,13 @@
             final long totalMemoryInKb = getTotalMemory() / 1000;
             final long memoryGrowthThreshold =
                     Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD);
-
-            for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                ProcessRecord proc = mProcessList.mLruProcesses.get(i);
+            mProcessList.forEachLruProcessesLOSP(false, proc -> {
                 final ProcessProfileRecord pr = proc.mProfile;
-                if (proc.notCachedSinceIdle) {
-                    if (proc.setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
-                            && proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
+                final ProcessStateRecord state = proc.mState;
+                final int setProcState = state.getSetProcState();
+                if (state.isNotCachedSinceIdle()) {
+                    if (setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
+                            && setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
                         final long initialIdlePss, lastPss, lastSwapPss;
                         synchronized (mAppProfiler.mProfilerLock) {
                             initialIdlePss = pr.getInitialIdlePss();
@@ -7065,39 +7229,43 @@
                             lastSwapPss = pr.getLastSwapPss();
                         }
                         if (doKilling && initialIdlePss != 0
-                                && lastPss > ((initialIdlePss * 3) / 2)
+                                && lastPss > (initialIdlePss * 3 / 2)
                                 && lastPss > (initialIdlePss + memoryGrowthThreshold)) {
-                            sb = new StringBuilder(128);
-                            sb.append("Kill");
-                            sb.append(proc.processName);
-                            sb.append(" in idle maint: pss=");
-                            sb.append(lastPss);
-                            sb.append(", swapPss=");
-                            sb.append(lastSwapPss);
-                            sb.append(", initialPss=");
-                            sb.append(initialIdlePss);
-                            sb.append(", period=");
-                            TimeUtils.formatDuration(timeSinceLastIdle, sb);
-                            sb.append(", lowRamPeriod=");
-                            TimeUtils.formatDuration(lowRamSinceLastIdle, sb);
-                            Slog.wtfQuiet(TAG, sb.toString());
-                            proc.kill("idle maint (pss " + lastPss
-                                    + " from " + initialIdlePss + ")",
-                                    ApplicationExitInfo.REASON_OTHER,
-                                    ApplicationExitInfo.SUBREASON_MEMORY_PRESSURE,
-                                    true);
+                            final StringBuilder sb2 = new StringBuilder(128);
+                            sb2.append("Kill");
+                            sb2.append(proc.processName);
+                            sb2.append(" in idle maint: pss=");
+                            sb2.append(lastPss);
+                            sb2.append(", swapPss=");
+                            sb2.append(lastSwapPss);
+                            sb2.append(", initialPss=");
+                            sb2.append(initialIdlePss);
+                            sb2.append(", period=");
+                            TimeUtils.formatDuration(timeSinceLastIdle, sb2);
+                            sb2.append(", lowRamPeriod=");
+                            TimeUtils.formatDuration(lowRamSinceLastIdle, sb2);
+                            Slog.wtfQuiet(TAG, sb2.toString());
+                            mHandler.post(() -> {
+                                synchronized (ActivityManagerService.this) {
+                                    proc.killLocked("idle maint (pss " + lastPss
+                                            + " from " + initialIdlePss + ")",
+                                            ApplicationExitInfo.REASON_OTHER,
+                                            ApplicationExitInfo.SUBREASON_MEMORY_PRESSURE,
+                                            true);
+                                }
+                            });
                         }
                     }
-                } else if (proc.setProcState < ActivityManager.PROCESS_STATE_HOME
-                        && proc.setProcState >= ActivityManager.PROCESS_STATE_PERSISTENT) {
-                    proc.notCachedSinceIdle = true;
+                } else if (setProcState < ActivityManager.PROCESS_STATE_HOME
+                        && setProcState >= ActivityManager.PROCESS_STATE_PERSISTENT) {
+                    state.setNotCachedSinceIdle(true);
                     synchronized (mAppProfiler.mProfilerLock) {
                         pr.setInitialIdlePss(0);
                         mAppProfiler.updateNextPssTimeLPf(
-                                proc.setProcState, proc.mProfile, now, true);
+                                state.getSetProcState(), proc.mProfile, now, true);
                     }
                 }
-            }
+            });
         }
     }
 
@@ -7208,7 +7376,7 @@
 
         synchronized(this) {
             if (procsToKill != null) {
-                for (int i=procsToKill.size()-1; i>=0; i--) {
+                for (int i = procsToKill.size() - 1; i >= 0; i--) {
                     ProcessRecord proc = procsToKill.get(i);
                     Slog.i(TAG, "Removing system update proc: " + proc);
                     mProcessList.removeProcessLocked(proc, true, false,
@@ -7435,16 +7603,18 @@
 
     private void updateForceBackgroundCheck(boolean enabled) {
         synchronized (this) {
-            if (mForceBackgroundCheck != enabled) {
-                mForceBackgroundCheck = enabled;
+            synchronized (mProcLock) {
+                if (mForceBackgroundCheck != enabled) {
+                    mForceBackgroundCheck = enabled;
 
-                if (DEBUG_BACKGROUND_CHECK) {
-                    Slog.i(TAG, "Force background check " + (enabled ? "enabled" : "disabled"));
-                }
+                    if (DEBUG_BACKGROUND_CHECK) {
+                        Slog.i(TAG, "Force background check " + (enabled ? "enabled" : "disabled"));
+                    }
 
-                if (mForceBackgroundCheck) {
-                    // Stop background services for idle UIDs.
-                    mProcessList.doStopUidForIdleUidsLocked();
+                    if (mForceBackgroundCheck) {
+                        // Stop background services for idle UIDs.
+                        mProcessList.doStopUidForIdleUidsLocked();
+                    }
                 }
             }
         }
@@ -7509,7 +7679,7 @@
                 (r != null) ? r.uid : -1,
                 eventType,
                 processName,
-                (r != null) ? r.pid : -1,
+                (r != null) ? r.getPid() : -1,
                 (r != null && r.info != null) ? r.info.packageName : "",
                 (r != null && r.info != null) ? (r.info.isInstantApp()
                         ? FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__TRUE
@@ -7755,8 +7925,8 @@
             return null;
         }
 
-        synchronized (this) {
-            return mProcessList.findAppProcessLocked(app, reason);
+        synchronized (mProcLock) {
+            return mProcessList.findAppProcessLOSP(app, reason);
         }
     }
 
@@ -7765,7 +7935,7 @@
      * to append various headers to the dropbox log text.
      */
     void appendDropBoxProcessHeaders(ProcessRecord process, String processName,
-            StringBuilder sb) {
+            final StringBuilder sb) {
         // Watchdog thread ends up invoking this function (with
         // a null ProcessRecord) to add the stack file to dropbox.
         // Do not acquire a lock on this (am) in such cases, as it
@@ -7779,17 +7949,18 @@
         // Note: ProcessRecord 'process' is guarded by the service
         // instance.  (notably process.pkgList, which could otherwise change
         // concurrently during execution of this method)
-        synchronized (this) {
+        synchronized (mProcLock) {
             sb.append("Process: ").append(processName).append("\n");
-            sb.append("PID: ").append(process.pid).append("\n");
+            sb.append("PID: ").append(process.getPid()).append("\n");
             sb.append("UID: ").append(process.uid).append("\n");
             int flags = process.info.flags;
-            IPackageManager pm = AppGlobals.getPackageManager();
+            final IPackageManager pm = AppGlobals.getPackageManager();
             sb.append("Flags: 0x").append(Integer.toHexString(flags)).append("\n");
+            final int callingUserId = UserHandle.getCallingUserId();
             process.getPkgList().forEachPackage(pkg -> {
                 sb.append("Package: ").append(pkg);
                 try {
-                    PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId());
+                    final PackageInfo pi = pm.getPackageInfo(pkg, 0, callingUserId);
                     if (pi != null) {
                         sb.append(" v").append(pi.getLongVersionCode());
                         if (pi.versionName != null) {
@@ -7808,7 +7979,7 @@
     }
 
     private static String processClass(ProcessRecord process) {
-        if (process == null || process.pid == MY_PID) {
+        if (process == null || process.getPid() == MY_PID) {
             return "system_server";
         } else if ((process.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
             return "system_app";
@@ -7870,8 +8041,8 @@
             sb.append("Foreground: ")
                     .append(process.isInterestingToUserLocked() ? "Yes" : "No")
                     .append("\n");
-            if (process.startTime > 0) {
-                long runtimeMillis = SystemClock.elapsedRealtime() - process.startTime;
+            if (process.getStartTime() > 0) {
+                long runtimeMillis = SystemClock.elapsedRealtime() - process.getStartTime();
                 sb.append("Process-Runtime: ").append(runtimeMillis).append("\n");
             }
         }
@@ -7879,7 +8050,7 @@
             sb.append("Activity: ").append(activityShortComponentName).append("\n");
         }
         if (parentShortComponentName != null) {
-            if (parentProcess != null && parentProcess.pid != process.pid) {
+            if (parentProcess != null && parentProcess.getPid() != process.getPid()) {
                 sb.append("Parent-Process: ").append(parentProcess.processName).append("\n");
             }
             if (!parentShortComponentName.equals(activityShortComponentName)) {
@@ -7975,47 +8146,46 @@
     public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
         enforceNotIsolatedCaller("getProcessesInErrorState");
         // assume our apps are happy - lazy create the list
-        List<ActivityManager.ProcessErrorStateInfo> errList = null;
+        final List<ActivityManager.ProcessErrorStateInfo>[] errList = new List[1];
 
         final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
                 Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
         int userId = UserHandle.getUserId(Binder.getCallingUid());
 
-        synchronized (this) {
-
+        synchronized (mProcLock) {
             // iterate across all processes
-            for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
-                ProcessRecord app = mProcessList.mLruProcesses.get(i);
+            mProcessList.forEachLruProcessesLOSP(false, app -> {
                 if (!allUsers && app.userId != userId) {
-                    continue;
+                    return;
                 }
-                final boolean crashing = app.isCrashing();
-                final boolean notResponding = app.isNotResponding();
-                if ((app.thread != null) && (crashing || notResponding)) {
+                final ProcessErrorStateRecord errState = app.mErrorState;
+                final boolean crashing = errState.isCrashing();
+                final boolean notResponding = errState.isNotResponding();
+                if ((app.getThread() != null) && (crashing || notResponding)) {
                     // This one's in trouble, so we'll generate a report for it
                     // crashes are higher priority (in case there's a crash *and* an anr)
                     ActivityManager.ProcessErrorStateInfo report = null;
                     if (crashing) {
-                        report = app.crashingReport;
+                        report = errState.getCrashingReport();
                     } else if (notResponding) {
-                        report = app.notRespondingReport;
+                        report = errState.getNotRespondingReport();
                     }
 
                     if (report != null) {
-                        if (errList == null) {
-                            errList = new ArrayList<>(1);
+                        if (errList[0] == null) {
+                            errList[0] = new ArrayList<>(1);
                         }
-                        errList.add(report);
+                        errList[0].add(report);
                     } else {
                         Slog.w(TAG, "Missing app error report, app = " + app.processName +
                                 " crashing = " + crashing +
                                 " notResponding = " + notResponding);
                     }
                 }
-            }
+            });
         }
 
-        return errList;
+        return errList[0];
     }
 
     @Override
@@ -8031,9 +8201,9 @@
         final boolean allUids = mAtmInternal.isGetTasksAllowed(
                 "getRunningAppProcesses", Binder.getCallingPid(), callingUid);
 
-        synchronized (this) {
+        synchronized (mProcLock) {
             // Iterate across all processes
-            return mProcessList.getRunningAppProcessesLocked(allUsers, userId, allUids,
+            return mProcessList.getRunningAppProcessesLOSP(allUsers, userId, allUids,
                     callingUid, clientTargetSdk);
         }
     }
@@ -8144,13 +8314,13 @@
         final int callingUid = Binder.getCallingUid();
         final int clientTargetSdk = mPackageManagerInt.getUidTargetSdkVersion(callingUid);
 
-        synchronized (this) {
+        synchronized (mProcLock) {
             ProcessRecord proc;
             synchronized (mPidsSelfLocked) {
                 proc = mPidsSelfLocked.get(Binder.getCallingPid());
             }
             if (proc != null) {
-                mProcessList.fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
+                mProcessList.fillInProcMemInfoLOSP(proc, outState, clientTargetSdk);
             }
         }
     }
@@ -8196,7 +8366,9 @@
 
         synchronized(this) {
             mConstants.dump(pw);
-            mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
+            synchronized (mProcLock) {
+                mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
+            }
             mOomAdjuster.dumpCacheOomRankerSettings(pw);
             pw.println();
             if (dumpAll) {
@@ -8328,7 +8500,9 @@
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
             }
-            mProcessList.dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
+            synchronized (mProcLock) {
+                mProcessList.dumpProcessesLSP(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
+            }
             pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
@@ -8432,7 +8606,9 @@
                 }
                 // output proto is ProcessProto
                 synchronized (this) {
-                    mProcessList.writeProcessesToProtoLocked(proto, dumpPackage);
+                    synchronized (mProcLock) {
+                        mProcessList.writeProcessesToProtoLSP(proto, dumpPackage);
+                    }
                 }
             } else {
                 // default option, dump everything, output is ActivityManagerServiceProto
@@ -8451,7 +8627,9 @@
                     proto.end(serviceToken);
 
                     long processToken = proto.start(ActivityManagerServiceProto.PROCESSES);
-                    mProcessList.writeProcessesToProtoLocked(proto, dumpPackage);
+                    synchronized (mProcLock) {
+                        mProcessList.writeProcessesToProtoLSP(proto, dumpPackage);
+                    }
                     proto.end(processToken);
                 }
             }
@@ -8525,8 +8703,10 @@
                     opti++;
                 }
                 synchronized (this) {
-                    mProcessList.dumpProcessesLocked(
-                            fd, pw, args, opti, true, dumpPackage, dumpAppId);
+                    synchronized (mProcLock) {
+                        mProcessList.dumpProcessesLSP(
+                                fd, pw, args, opti, true, dumpPackage, dumpAppId);
+                    }
                 }
             } else if ("oom".equals(cmd) || "o".equals(cmd)) {
                 synchronized (this) {
@@ -8614,6 +8794,8 @@
             } else if ("settings".equals(cmd)) {
                 synchronized (this) {
                     mConstants.dump(pw);
+                }
+                synchronized (mProcLock) {
                     mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
                     mOomAdjuster.dumpCacheOomRankerSettings(pw);
                 }
@@ -8863,8 +9045,8 @@
         return needSep;
     }
 
-    @GuardedBy("this")
-    void dumpOtherProcessesInfoLocked(FileDescriptor fd, PrintWriter pw,
+    @GuardedBy({"this", "mProcLock"})
+    void dumpOtherProcessesInfoLSP(FileDescriptor fd, PrintWriter pw,
             boolean dumpAll, String dumpPackage, int dumpAppId, int numPers, boolean needSep) {
         if (dumpAll || dumpPackage != null) {
             final SparseArray<ProcessRecord> pidToProcess = new SparseArray<>();
@@ -8872,7 +9054,7 @@
                 boolean printed = false;
                 for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) {
                     ProcessRecord r = mPidsSelfLocked.valueAt(i);
-                    pidToProcess.put(r.pid, r);
+                    pidToProcess.put(r.getPid(), r);
                     if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
                         continue;
                     }
@@ -8883,7 +9065,7 @@
                         printed = true;
                     }
                     pw.print("    PID #"); pw.print(mPidsSelfLocked.keyAt(i));
-                        pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
+                    pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
                 }
             }
 
@@ -8923,8 +9105,7 @@
             synchronized (mPidsSelfLocked) {
                 boolean printed = false;
                 for (int i = 0, size = mImportantProcesses.size(); i < size; i++) {
-                    ProcessRecord r = mPidsSelfLocked.get(
-                            mImportantProcesses.valueAt(i).pid);
+                    ProcessRecord r = mPidsSelfLocked.get(mImportantProcesses.valueAt(i).pid);
                     if (dumpPackage != null && (r == null
                             || !r.getPkgList().containsKey(dumpPackage))) {
                         continue;
@@ -8965,7 +9146,7 @@
                     "OnHold Norm", "OnHold PERS", dumpPackage);
         }
 
-        needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage);
+        needSep = mAppErrors.dumpLPr(fd, pw, needSep, dumpPackage);
 
         needSep = mAtmInternal.dumpForProcesses(fd, pw, dumpAll, dumpPackage, dumpAppId, needSep,
                 mAppProfiler.getTestPssMode(), mWakefulness.get());
@@ -9060,7 +9241,7 @@
                         TimeUtils.formatDuration(now, mLastIdleTime, pw);
                         pw.print(" mLowRamSinceLastIdle=");
                         TimeUtils.formatDuration(
-                                mAppProfiler.getLowRamTimeSinceIdleLocked(now), pw);
+                                mAppProfiler.getLowRamTimeSinceIdleLPr(now), pw);
                         pw.println();
 
                 pw.println();
@@ -9077,8 +9258,8 @@
         mUserController.dump(pw);
     }
 
-    @GuardedBy("this")
-    void writeOtherProcessesInfoToProtoLocked(ProtoOutputStream proto, String dumpPackage,
+    @GuardedBy({"this", "mProcLock"})
+    void writeOtherProcessesInfoToProtoLSP(ProtoOutputStream proto, String dumpPackage,
             int dumpAppId, int numPers) {
         for (int i = 0, size = mActiveInstrumentation.size(); i < size; i++) {
             ActiveInstrumentation ai = mActiveInstrumentation.get(i);
@@ -9095,7 +9276,7 @@
 
         if (dumpPackage != null) {
             synchronized (mPidsSelfLocked) {
-                for (int i=0; i<mPidsSelfLocked.size(); i++) {
+                for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) {
                     ProcessRecord r = mPidsSelfLocked.valueAt(i);
                     if (!r.getPkgList().containsKey(dumpPackage)) {
                         continue;
@@ -9151,7 +9332,7 @@
                     ActivityManagerServiceDumpProcessesProto.GC_PROCS,
                     dumpPackage);
         }
-        mAppErrors.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS,
+        mAppErrors.dumpDebugLPr(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS,
                 dumpPackage);
         mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness.get(),
                 mAppProfiler.getTestPssMode());
@@ -9224,7 +9405,7 @@
             long now = SystemClock.uptimeMillis();
             ProtoUtils.toDuration(proto, ActivityManagerServiceDumpProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
             proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS,
-                    mAppProfiler.getLowRamTimeSinceIdleLocked(now));
+                    mAppProfiler.getLowRamTimeSinceIdleLPr(now));
         }
     }
 
@@ -9571,14 +9752,13 @@
         mUgmInternal.dump(pw, dumpAll, dumpPackage);
     }
 
-    private static final int dumpProcessList(PrintWriter pw,
+    private static int dumpProcessList(PrintWriter pw,
             ActivityManagerService service, List list,
             String prefix, String normalLabel, String persistentLabel,
             String dumpPackage) {
         int numPers = 0;
-        final int N = list.size()-1;
-        for (int i=N; i>=0; i--) {
-            ProcessRecord r = (ProcessRecord)list.get(i);
+        for (int i = list.size() - 1; i >= 0; i--) {
+            ProcessRecord r = (ProcessRecord) list.get(i);
             if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
                 continue;
             }
@@ -9594,8 +9774,8 @@
 
     ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, boolean allPkgs,
             String[] args) {
-        synchronized (this) {
-            return mProcessList.collectProcessesLocked(start, allPkgs, args);
+        synchronized (mProcLock) {
+            return mProcessList.collectProcessesLOSP(start, allPkgs, args);
         }
     }
 
@@ -9614,13 +9794,15 @@
 
         for (int i = procs.size() - 1 ; i >= 0 ; i--) {
             ProcessRecord r = procs.get(i);
-            if (r.thread != null) {
-                pw.println("\n** Graphics info for pid " + r.pid + " [" + r.processName + "] **");
+            final int pid = r.getPid();
+            final IApplicationThread thread = r.getThread();
+            if (thread != null) {
+                pw.println("\n** Graphics info for pid " + pid + " [" + r.processName + "] **");
                 pw.flush();
                 try {
                     TransferPipe tp = new TransferPipe();
                     try {
-                        r.thread.dumpGfxInfo(tp.getWriteFd(), args);
+                        thread.dumpGfxInfo(tp.getWriteFd(), args);
                         tp.go(fd);
                     } finally {
                         tp.kill();
@@ -9647,13 +9829,15 @@
 
         for (int i = procs.size() - 1; i >= 0; i--) {
             ProcessRecord r = procs.get(i);
-            if (r.thread != null) {
-                pw.println("\n\n** Cache info for pid " + r.pid + " [" + r.processName + "] **");
+            final int pid = r.getPid();
+            final IApplicationThread thread = r.getThread();
+            if (thread != null) {
+                pw.println("\n\n** Cache info for pid " + pid + " [" + r.processName + "] **");
                 pw.flush();
                 try {
                     TransferPipe tp = new TransferPipe();
                     try {
-                        r.thread.dumpCacheInfo(tp.getWriteFd(), args);
+                        thread.dumpCacheInfo(tp.getWriteFd(), args);
                         tp.go(fd);
                     } finally {
                         tp.kill();
@@ -9680,13 +9864,15 @@
 
         for (int i = procs.size() - 1 ; i >= 0 ; i--) {
             ProcessRecord r = procs.get(i);
-            if (r.thread != null) {
-                pw.println("\n** Database info for pid " + r.pid + " [" + r.processName + "] **");
+            final int pid = r.getPid();
+            final IApplicationThread thread = r.getThread();
+            if (thread != null) {
+                pw.println("\n** Database info for pid " + pid + " [" + r.processName + "] **");
                 pw.flush();
                 try {
                     TransferPipe tp = new TransferPipe();
                     try {
-                        r.thread.dumpDbInfo(tp.getWriteFd(), args);
+                        thread.dumpDbInfo(tp.getWriteFd(), args);
                         tp.go(fd);
                     } finally {
                         tp.kill();
@@ -10149,10 +10335,10 @@
             final int pid;
             final int oomAdj;
             final boolean hasActivities;
-            synchronized (this) {
-                thread = r.thread;
-                pid = r.pid;
-                oomAdj = r.getSetAdjWithServices();
+            synchronized (mProcLock) {
+                thread = r.getThread();
+                pid = r.getPid();
+                oomAdj = r.mState.getSetAdjWithServices();
                 hasActivities = r.hasActivities();
             }
             if (thread != null) {
@@ -10223,8 +10409,8 @@
                 final long myTotalRss = mi.getTotalRss();
                 final long myTotalSwapPss = mi.getTotalSwappedOutPss();
 
-                synchronized (this) {
-                    if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
+                synchronized (mProcLock) {
+                    if (r.getThread() != null && oomAdj == r.mState.getSetAdjWithServices()) {
                         // Record this for posterity if the process has been stable.
                         r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
                                 reportType, endTime - startTime);
@@ -10734,10 +10920,10 @@
             final int pid;
             final int oomAdj;
             final boolean hasActivities;
-            synchronized (this) {
-                thread = r.thread;
-                pid = r.pid;
-                oomAdj = r.getSetAdjWithServices();
+            synchronized (mProcLock) {
+                thread = r.getThread();
+                pid = r.getPid();
+                oomAdj = r.mState.getSetAdjWithServices();
                 hasActivities = r.hasActivities();
             }
             if (thread == null) {
@@ -10803,8 +10989,8 @@
             final long myTotalRss = mi.getTotalRss();
             final long myTotalSwapPss = mi.getTotalSwappedOutPss();
 
-            synchronized (this) {
-                if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
+            synchronized (mProcLock) {
+                if (r.getThread() != null && oomAdj == r.mState.getSetAdjWithServices()) {
                     // Record this for posterity if the process has been stable.
                     r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
                                 reportType, endTime - startTime);
@@ -11143,96 +11329,26 @@
      * app that was passed in must remain on the process lists.
      */
     @GuardedBy("this")
-    final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
+    final boolean cleanUpApplicationRecordLocked(ProcessRecord app, int pid,
             boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
-        if (index >= 0) {
-            removeLruProcessLocked(app);
-            ProcessList.remove(app.pid);
-        }
+        boolean restart;
+        synchronized (mProcLock) {
+            if (index >= 0) {
+                removeLruProcessLocked(app);
+                ProcessList.remove(pid);
+            }
 
+            restart = app.onCleanupApplicationRecordLSP(mProcessStats, allowRestart);
+        }
         mAppProfiler.onCleanupApplicationRecordLocked(app);
-
-        // Dismiss any open dialogs.
-        app.getDialogController().clearAllErrorDialogs();
-
-        app.setCrashing(false);
-        app.setNotResponding(false);
-
-        app.resetPackageList(mProcessStats);
-        app.unlinkDeathRecipient();
-        app.makeInactive(mProcessStats);
-        app.waitingToKill = null;
-        app.forcingToImportant = null;
-        updateProcessForegroundLocked(app, false, 0, false);
-        app.setHasForegroundActivities(false);
-        app.hasShownUi = false;
-        app.treatLikeActivity = false;
-        app.hasAboveClient = false;
-        app.setHasClientActivities(false);
-
-        mServices.killServicesLocked(app, allowRestart);
-        mPhantomProcessList.onAppDied(app.pid);
-
-        boolean restart = false;
-
-        // Remove published content providers.
-        for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
-            ContentProviderRecord cpr = app.pubProviders.valueAt(i);
-            if (cpr.proc != app) {
-                // If the hosting process record isn't really us, bail out
-                continue;
-            }
-            final boolean alwaysRemove = app.bad || !allowRestart;
-            final boolean inLaunching = mCpHelper.removeDyingProviderLocked(app, cpr, alwaysRemove);
-            if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {
-                // We left the provider in the launching list, need to
-                // restart it.
-                restart = true;
-            }
-
-            cpr.provider = null;
-            cpr.setProcess(null);
-        }
-        app.pubProviders.clear();
-
-        // Take care of any launching providers waiting for this process.
-        if (mCpHelper.cleanupAppInLaunchingProvidersLocked(app, false)) {
-            mProcessList.noteProcessDiedLocked(app);
-            restart = true;
-        }
-
-        // Unregister from connected content providers.
-        if (!app.conProviders.isEmpty()) {
-            for (int i = app.conProviders.size() - 1; i >= 0; i--) {
-                ContentProviderConnection conn = app.conProviders.get(i);
-                conn.provider.connections.remove(conn);
-                stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
-                        conn.provider.appInfo.longVersionCode, conn.provider.name,
-                        conn.provider.info.processName);
-            }
-            app.conProviders.clear();
-        }
-
-        // At this point there may be remaining entries in mLaunchingProviders
-        // where we were the only one waiting, so they are no longer of use.
-        // Look for these and clean up if found.
-        // XXX Commented out for now.  Trying to figure out a way to reproduce
-        // the actual situation to identify what is actually going on.
-        if (false) {
-            mCpHelper.cleanupLaunchingProvidersLocked();
-        }
-
         skipCurrentReceiverLocked(app);
-
-        // Unregister any receivers.
-        for (int i = app.receivers.size() - 1; i >= 0; i--) {
-            removeReceiverLocked(app.receivers.valueAt(i));
-        }
-        app.receivers.clear();
+        updateProcessForegroundLocked(app, false, 0, false);
+        mServices.killServicesLocked(app, allowRestart);
+        mPhantomProcessList.onAppDied(pid);
 
         // If the app is undergoing backup, tell the backup manager about it
         final BackupRecord backupTarget = mBackupTargets.get(app.userId);
-        if (backupTarget != null && app.pid == backupTarget.app.pid) {
+        if (backupTarget != null && pid == backupTarget.app.getPid()) {
             if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
                     + backupTarget.appInfo + " died during backup");
             mHandler.post(new Runnable() {
@@ -11249,9 +11365,9 @@
             });
         }
 
-        mProcessList.scheduleDispatchProcessDiedLocked(app.pid, app.info.uid);
+        mProcessList.scheduleDispatchProcessDiedLocked(pid, app.info.uid);
 
-        // If this is a precede instance of another process instance
+        // If this is a preceding instance of another process instance
         allowRestart = true;
         synchronized (app) {
             if (app.mSuccessor != null) {
@@ -11259,13 +11375,13 @@
                 // because we have created a new one already.
                 allowRestart = false;
                 // If it's persistent, add the successor to mPersistentStartingProcesses
-                if (app.isPersistent() && !app.removed) {
+                if (app.isPersistent() && !app.isRemoved()) {
                     if (mPersistentStartingProcesses.indexOf(app.mSuccessor) < 0) {
                         mPersistentStartingProcesses.add(app.mSuccessor);
                     }
                 }
                 // clean up the field so the successor's proc starter could proceed.
-                app.mSuccessor.mPrecedence = null;
+                app.mSuccessor.mPredecessor = null;
                 app.mSuccessor = null;
                 // Notify if anyone is waiting for it.
                 app.notifyAll();
@@ -11285,7 +11401,7 @@
                 mProcessList.removeProcessNameLocked(app.processName, app.uid, app);
             }
             mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
-        } else if (!app.removed) {
+        } else if (!app.isRemoved()) {
             // This app is persistent, so we need to keep its record around.
             // If it is not already on the pending app list, add it there
             // and start a new process for it.
@@ -11305,7 +11421,7 @@
             // We have components that still need to be running in the
             // process, so re-launch it.
             if (index < 0) {
-                ProcessList.remove(app.pid);
+                ProcessList.remove(pid);
             }
 
             // Remove provider publish timeout because we will start a new timeout when the
@@ -11313,14 +11429,13 @@
             mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, app);
 
             mProcessList.addProcessNameLocked(app);
-            app.pendingStart = false;
-            mProcessList.startProcessLocked(app,
-                    new HostingRecord("restart", app.processName),
+            app.setPendingStart(false);
+            mProcessList.startProcessLocked(app, new HostingRecord("restart", app.processName),
                     ZYGOTE_POLICY_FLAG_EMPTY);
             return true;
-        } else if (app.pid > 0 && app.pid != MY_PID) {
+        } else if (pid > 0 && pid != MY_PID) {
             // Goodbye!
-            removePidLocked(app);
+            removePidLocked(pid, app);
             mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
             mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
             if (app.isolated) {
@@ -11659,12 +11774,12 @@
             // process after the full backup is done and the ProcessRecord will vaporize anyway.
             if (UserHandle.isApp(app.uid) &&
                     backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL) {
-                proc.inFullBackup = true;
+                proc.setInFullBackup(true);
             }
             r.app = proc;
             final BackupRecord backupTarget = mBackupTargets.get(targetUserId);
             oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
-            newBackupUid = proc.inFullBackup ? r.appInfo.uid : -1;
+            newBackupUid = proc.isInFullBackup() ? r.appInfo.uid : -1;
             mBackupTargets.put(targetUserId, r);
 
             // Try not to kill the process during backup
@@ -11672,10 +11787,11 @@
 
             // If the process is already attached, schedule the creation of the backup agent now.
             // If it is not yet live, this will be done when it attaches to the framework.
-            if (proc.thread != null) {
+            final IApplicationThread thread = proc.getThread();
+            if (thread != null) {
                 if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc already running: " + proc);
                 try {
-                    proc.thread.scheduleCreateBackupAgent(app,
+                    thread.scheduleCreateBackupAgent(app,
                             compatibilityInfoForPackage(app), backupMode, targetUserId,
                             operationType);
                 } catch (RemoteException e) {
@@ -11785,14 +11901,15 @@
                 // Not backing this app up any more; reset its OOM adjustment
                 final ProcessRecord proc = backupTarget.app;
                 updateOomAdjLocked(proc, true, OomAdjuster.OOM_ADJ_REASON_NONE);
-                proc.inFullBackup = false;
+                proc.setInFullBackup(false);
 
                 oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
 
                 // If the app crashed during backup, 'thread' will be null here
-                if (proc.thread != null) {
+                final IApplicationThread thread = proc.getThread();
+                if (thread != null) {
                     try {
-                        proc.thread.scheduleDestroyBackupAgent(appInfo,
+                        thread.scheduleDestroyBackupAgent(appInfo,
                                 compatibilityInfoForPackage(appInfo), userId);
                     } catch (Exception e) {
                         Slog.e(TAG, "Exception when unbinding backup agent:");
@@ -11887,7 +12004,7 @@
         boolean instantApp;
         synchronized(this) {
             if (caller != null) {
-                callerApp = getRecordForAppLocked(caller);
+                callerApp = getRecordForAppLOSP(caller);
                 if (callerApp == null) {
                     throw new SecurityException(
                             "Unable to find app for caller " + caller
@@ -11901,7 +12018,7 @@
                             + " is not running in process " + callerApp);
                 }
                 callingUid = callerApp.info.uid;
-                callingPid = callerApp.pid;
+                callingPid = callerApp.getPid();
             } else {
                 callerPackage = null;
                 callingUid = Binder.getCallingUid();
@@ -11970,8 +12087,9 @@
         }
 
         synchronized (this) {
-            if (callerApp != null && (callerApp.thread == null
-                    || callerApp.thread.asBinder() != caller.asBinder())) {
+            IApplicationThread thread;
+            if (callerApp != null && ((thread = callerApp.getThread()) == null
+                    || thread.asBinder() != caller.asBinder())) {
                 // Original caller already died
                 return null;
             }
@@ -11980,13 +12098,13 @@
                 rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                         userId, receiver);
                 if (rl.app != null) {
-                    final int totalReceiversForApp = rl.app.receivers.size();
+                    final int totalReceiversForApp = rl.app.mReceivers.numberOfReceivers();
                     if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
                         throw new IllegalStateException("Too many receivers, total of "
                                 + totalReceiversForApp + ", registered for pid: "
                                 + rl.pid + ", callerPackage: " + callerPackage);
                     }
-                    rl.app.receivers.add(rl);
+                    rl.app.mReceivers.addReceiver(rl);
                 } else {
                     try {
                         receiver.asBinder().linkToDeath(rl, 0);
@@ -12072,7 +12190,7 @@
                     }
 
                     if (rl.app != null) {
-                        rl.app.receivers.remove(rl);
+                        rl.app.mReceivers.removeReceiver(rl);
                     }
                     removeReceiverLocked(rl);
                     if (rl.linkedToDeath) {
@@ -12371,7 +12489,7 @@
                 }
             }
             if (brOptions.isDontSendToRestrictedApps()
-                    && !isUidActiveLocked(callingUid)
+                    && !isUidActiveLOSP(callingUid)
                     && isBackgroundRestrictedNoCheck(callingUid, callerPackage)) {
                 Slog.i(TAG, "Not sending broadcast " + action + " - app " + callerPackage
                         + " has background restrictions");
@@ -12578,12 +12696,14 @@
                                     if (killProcess) {
                                         final int extraUid = intent.getIntExtra(Intent.EXTRA_UID,
                                                 -1);
-                                        mProcessList.killPackageProcessesLocked(ssp,
-                                                UserHandle.getAppId(extraUid),
-                                                userId, ProcessList.INVALID_ADJ,
-                                                ApplicationExitInfo.REASON_USER_REQUESTED,
-                                                ApplicationExitInfo.SUBREASON_UNKNOWN,
-                                                "change " + ssp);
+                                        synchronized (mProcLock) {
+                                            mProcessList.killPackageProcessesLSP(ssp,
+                                                    UserHandle.getAppId(extraUid),
+                                                    userId, ProcessList.INVALID_ADJ,
+                                                    ApplicationExitInfo.REASON_USER_REQUESTED,
+                                                    ApplicationExitInfo.SUBREASON_UNKNOWN,
+                                                    "change " + ssp);
+                                        }
                                     }
                                     cleanupDisabledPackageComponentsLocked(ssp, userId,
                                             intent.getStringArrayExtra(
@@ -12727,7 +12847,7 @@
                     Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
                 final int uid = getUidFromIntent(intent);
                 if (uid != -1) {
-                    final UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
+                    final UidRecord uidRec = mProcessList.getUidRecordLOSP(uid);
                     if (uidRec != null) {
                         uidRec.updateHasInternetPermission();
                     }
@@ -13112,7 +13232,7 @@
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
 
-            final ProcessRecord callerApp = getRecordForAppLocked(caller);
+            final ProcessRecord callerApp = getRecordForAppLOSP(caller);
             final int callingPid = Binder.getCallingPid();
             final int callingUid = Binder.getCallingUid();
 
@@ -13352,27 +13472,29 @@
             final long origId = Binder.clearCallingIdentity();
 
             ProcessRecord app;
-            if (noRestart) {
-                app = getProcessRecordLocked(ai.processName, ai.uid, true);
-            } else {
-                // Instrumentation can kill and relaunch even persistent processes
-                forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
-                        "start instr");
-                // Inform usage stats to make the target package active
-                if (mUsageStatsService != null) {
-                    mUsageStatsService.reportEvent(ii.targetPackage, userId,
-                            UsageEvents.Event.SYSTEM_INTERACTION);
+            synchronized (mProcLock) {
+                if (noRestart) {
+                    app = getProcessRecordLocked(ai.processName, ai.uid, true);
+                } else {
+                    // Instrumentation can kill and relaunch even persistent processes
+                    forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false,
+                            userId, "start instr");
+                    // Inform usage stats to make the target package active
+                    if (mUsageStatsService != null) {
+                        mUsageStatsService.reportEvent(ii.targetPackage, userId,
+                                UsageEvents.Event.SYSTEM_INTERACTION);
+                    }
+                    app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, abiOverride,
+                            ZYGOTE_POLICY_FLAG_EMPTY);
                 }
-                app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, abiOverride,
-                        ZYGOTE_POLICY_FLAG_EMPTY);
-            }
 
-            app.setActiveInstrumentation(activeInstr);
-            activeInstr.mFinished = false;
-            activeInstr.mSourceUid = callingUid;
-            activeInstr.mRunningProcesses.add(app);
-            if (!mActiveInstrumentation.contains(activeInstr)) {
-                mActiveInstrumentation.add(activeInstr);
+                app.setActiveInstrumentation(activeInstr);
+                activeInstr.mFinished = false;
+                activeInstr.mSourceUid = callingUid;
+                activeInstr.mRunningProcesses.add(app);
+                if (!mActiveInstrumentation.contains(activeInstr)) {
+                    mActiveInstrumentation.add(activeInstr);
+                }
             }
 
             if ((flags & INSTR_FLAG_DISABLE_ISOLATED_STORAGE) != 0) {
@@ -13399,7 +13521,7 @@
         }
 
         try {
-            pr.thread.instrumentWithoutRestart(
+            pr.getThread().instrumentWithoutRestart(
                     activeInstr.mClass,
                     activeInstr.mArguments,
                     activeInstr.mWatcher,
@@ -13459,7 +13581,7 @@
         }
 
         synchronized(this) {
-            ProcessRecord app = getRecordForAppLocked(target);
+            ProcessRecord app = getRecordForAppLOSP(target);
             if (app == null) {
                 Slog.w(TAG, "addInstrumentationResults: no app for " + target);
                 return;
@@ -13481,36 +13603,38 @@
             return;
         }
 
-        if (!instr.mFinished) {
-            if (instr.mWatcher != null) {
-                Bundle finalResults = instr.mCurResults;
-                if (finalResults != null) {
-                    if (instr.mCurResults != null && results != null) {
-                        finalResults.putAll(results);
+        synchronized (mProcLock) {
+            if (!instr.mFinished) {
+                if (instr.mWatcher != null) {
+                    Bundle finalResults = instr.mCurResults;
+                    if (finalResults != null) {
+                        if (instr.mCurResults != null && results != null) {
+                            finalResults.putAll(results);
+                        }
+                    } else {
+                        finalResults = results;
                     }
-                } else {
-                    finalResults = results;
+                    mInstrumentationReporter.reportFinished(instr.mWatcher,
+                            instr.mClass, resultCode, finalResults);
                 }
-                mInstrumentationReporter.reportFinished(instr.mWatcher,
-                        instr.mClass, resultCode, finalResults);
+
+                // Can't call out of the system process with a lock held, so post a message.
+                if (instr.mUiAutomationConnection != null) {
+                    // Go back to the default mode of denying OP_NO_ISOLATED_STORAGE app op.
+                    mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid,
+                            app.info.packageName, AppOpsManager.MODE_ERRORED);
+                    mAppOpsService.setAppOpsServiceDelegate(null);
+                    getPermissionManagerInternal().stopShellPermissionIdentityDelegation();
+                    mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
+                            instr.mUiAutomationConnection).sendToTarget();
+                }
+                instr.mFinished = true;
             }
 
-            // Can't call out of the system process with a lock held, so post a message.
-            if (instr.mUiAutomationConnection != null) {
-                // Go back to the default mode of denying OP_NO_ISOLATED_STORAGE app op.
-                mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid,
-                        app.info.packageName, AppOpsManager.MODE_ERRORED);
-                mAppOpsService.setAppOpsServiceDelegate(null);
-                getPermissionManagerInternal().stopShellPermissionIdentityDelegation();
-                mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
-                        instr.mUiAutomationConnection).sendToTarget();
-            }
-            instr.mFinished = true;
+            instr.removeProcess(app);
+            app.setActiveInstrumentation(null);
         }
 
-        instr.removeProcess(app);
-        app.setActiveInstrumentation(null);
-
         if (!instr.mNoRestart) {
             forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false,
                     app.userId,
@@ -13546,7 +13670,7 @@
         }
 
         synchronized(this) {
-            ProcessRecord app = getRecordForAppLocked(target);
+            ProcessRecord app = getRecordForAppLOSP(target);
             if (app == null) {
                 Slog.w(TAG, "finishInstrumentation: no app for " + target);
                 return;
@@ -13652,10 +13776,11 @@
     // the current [or imminent] receiver on.
     boolean isReceivingBroadcastLocked(ProcessRecord app,
             ArraySet<BroadcastQueue> receivingQueues) {
-        final int N = app.curReceivers.size();
-        if (N > 0) {
-            for (int i = 0; i < N; i++) {
-                receivingQueues.add(app.curReceivers.valueAt(i).queue);
+        final ProcessReceiverRecord prr = app.mReceivers;
+        final int numOfReceivers = prr.numberOfCurReceivers();
+        if (numOfReceivers > 0) {
+            for (int i = 0; i < numOfReceivers; i++) {
+                receivingQueues.add(prr.getCurReceiverAt(i).queue);
             }
             return true;
         }
@@ -13774,6 +13899,7 @@
     /**
      * Returns true if things are idle enough to perform GCs.
      */
+    @GuardedBy("this")
     final boolean canGcNowLocked() {
         for (BroadcastQueue q : mBroadcastQueues) {
             if (!q.mParallelBroadcasts.isEmpty() || !q.mDispatcher.isEmpty()) {
@@ -13786,77 +13912,91 @@
     private void checkExcessivePowerUsage() {
         updateCpuStatsNow();
 
-        synchronized (this) {
-            boolean doCpuKills = true;
-            if (mLastPowerCheckUptime == 0) {
-                doCpuKills = false;
-            }
+        synchronized (mProcLock) {
+            final boolean doCpuKills = mLastPowerCheckUptime != 0;
             final long curUptime = SystemClock.uptimeMillis();
             final long uptimeSince = curUptime - mLastPowerCheckUptime;
             mLastPowerCheckUptime = curUptime;
-            int i = mProcessList.mLruProcesses.size();
-            while (i > 0) {
-                i--;
-                final ProcessRecord app = mProcessList.mLruProcesses.get(i);
-                if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
+            mProcessList.forEachLruProcessesLOSP(false, app -> {
+                if (app.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) {
                     int cpuLimit;
-                    long checkDur = curUptime - app.getWhenUnimportant();
+                    long checkDur = curUptime - app.mState.getWhenUnimportant();
                     if (checkDur <= mConstants.POWER_CHECK_INTERVAL) {
                         cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1;
                     } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 2)
-                            || app.setProcState <= ActivityManager.PROCESS_STATE_HOME) {
+                            || app.mState.getSetProcState() <= ActivityManager.PROCESS_STATE_HOME) {
                         cpuLimit = mConstants.POWER_CHECK_MAX_CPU_2;
                     } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 3)) {
                         cpuLimit = mConstants.POWER_CHECK_MAX_CPU_3;
                     } else {
                         cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4;
                     }
-                    synchronized (mAppProfiler.mProfilerLock) {
-                        final ProcessProfileRecord profile = app.mProfile;
-                        final long curCpuTime = profile.mCurCpuTime.get();
-                        final long lastCpuTime = profile.mLastCpuTime.get();
-                        if (lastCpuTime > 0) {
-                            final long cputimeUsed = curCpuTime - lastCpuTime;
-                            if (checkExcessivePowerUsageLocked(uptimeSince, doCpuKills, cputimeUsed,
-                                        app.processName, app.toShortString(), cpuLimit, app)) {
-                                app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince
-                                        + " dur=" + checkDur + " limit=" + cpuLimit,
-                                        ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
-                                        ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
-                                        true);
-                                profile.reportExcessiveCpu();
-                            }
-                        }
-                        profile.mLastCpuTime.set(curCpuTime);
-                    }
 
+                    updateAppProcessCpuTimeLPr(uptimeSince, doCpuKills, checkDur, cpuLimit, app);
 
                     // Also check the phantom processes if there is any
-                    final long chkDur = checkDur;
-                    final int cpuLmt = cpuLimit;
-                    final boolean doKill = doCpuKills;
-                    mPhantomProcessList.forEachPhantomProcessOfApp(app, r -> {
-                        if (r.mLastCputime > 0) {
-                            final long cputimeUsed = r.mCurrentCputime - r.mLastCputime;
-                            if (checkExcessivePowerUsageLocked(uptimeSince, doKill, cputimeUsed,
-                                    app.processName, r.toString(), cpuLimit, app)) {
-                                mPhantomProcessList.killPhantomProcessGroupLocked(app, r,
-                                        ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
-                                        ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
-                                        "excessive cpu " + cputimeUsed + " during "
-                                        + uptimeSince + " dur=" + chkDur + " limit=" + cpuLmt);
-                                return false;
-                            }
-                        }
-                        r.mLastCputime = r.mCurrentCputime;
-                        return true;
-                    });
+                    updatePhantomProcessCpuTimeLPr(
+                            uptimeSince, doCpuKills, checkDur, cpuLimit, app);
                 }
-            }
+            });
         }
     }
 
-    private boolean checkExcessivePowerUsageLocked(final long uptimeSince, boolean doCpuKills,
+    @GuardedBy("mProcLock")
+    private void updateAppProcessCpuTimeLPr(final long uptimeSince, final boolean doCpuKills,
+            final long checkDur, final int cpuLimit, final ProcessRecord app) {
+        synchronized (mAppProfiler.mProfilerLock) {
+            final ProcessProfileRecord profile = app.mProfile;
+            final long curCpuTime = profile.mCurCpuTime.get();
+            final long lastCpuTime = profile.mLastCpuTime.get();
+            if (lastCpuTime > 0) {
+                final long cpuTimeUsed = curCpuTime - lastCpuTime;
+                if (checkExcessivePowerUsageLPr(uptimeSince, doCpuKills, cpuTimeUsed,
+                            app.processName, app.toShortString(), cpuLimit, app)) {
+                    mHandler.post(() -> {
+                        synchronized (ActivityManagerService.this) {
+                            app.killLocked("excessive cpu " + cpuTimeUsed + " during "
+                                    + uptimeSince + " dur=" + checkDur + " limit=" + cpuLimit,
+                                    ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
+                                    ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
+                                    true);
+                        }
+                    });
+                    profile.reportExcessiveCpu();
+                }
+            }
+
+            profile.mLastCpuTime.set(curCpuTime);
+        }
+    }
+
+    @GuardedBy("mProcLock")
+    private void updatePhantomProcessCpuTimeLPr(final long uptimeSince, final boolean doCpuKills,
+            final long checkDur, final int cpuLimit, final ProcessRecord app) {
+        mPhantomProcessList.forEachPhantomProcessOfApp(app, r -> {
+            if (r.mLastCputime > 0) {
+                final long cpuTimeUsed = r.mCurrentCputime - r.mLastCputime;
+                if (checkExcessivePowerUsageLPr(uptimeSince, doCpuKills, cpuTimeUsed,
+                            app.processName, r.toString(), cpuLimit, app)) {
+                    mHandler.post(() -> {
+                        synchronized (ActivityManagerService.this) {
+                            mPhantomProcessList.killPhantomProcessGroupLocked(app, r,
+                                    ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
+                                    ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
+                                    "excessive cpu " + cpuTimeUsed + " during "
+                                    + uptimeSince + " dur=" + checkDur + " limit=" + cpuLimit);
+                        }
+                    });
+                    return false;
+                }
+            }
+            r.mLastCputime = r.mCurrentCputime;
+            return true;
+        });
+    }
+
+    @GuardedBy("mProcLock")
+    private boolean checkExcessivePowerUsageLPr(final long uptimeSince, boolean doCpuKills,
             final long cputimeUsed, final String processName, final String description,
             final int cpuLimit, final ProcessRecord app) {
         if (DEBUG_POWER) {
@@ -13901,19 +14041,20 @@
                 UserHandle.getUserId(uid), packages[0]);
     }
 
+    @GuardedBy("this")
     void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
-        uid = uidRec != null ? uidRec.uid : uid;
+        uid = uidRec != null ? uidRec.getUid() : uid;
         if (uid < 0) {
             throw new IllegalArgumentException("No UidRecord or uid");
         }
 
         final int procState = uidRec != null
-                ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
+                ? uidRec.getSetProcState() : PROCESS_STATE_NONEXISTENT;
         final long procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
-        final int capability = uidRec != null ? uidRec.setCapability : 0;
-        final boolean ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
+        final int capability = uidRec != null ? uidRec.getSetCapability() : 0;
+        final boolean ephemeral = uidRec != null ? uidRec.isEphemeral() : isEphemeralLocked(uid);
 
-        if (uidRec != null && !uidRec.idle && (change & UidRecord.CHANGE_GONE) != 0) {
+        if (uidRec != null && !uidRec.isIdle() && (change & UidRecord.CHANGE_GONE) != 0) {
             // If this uid is going away, and we haven't yet reported it is gone,
             // then do so now.
             change |= UidRecord.CHANGE_IDLE;
@@ -13922,7 +14063,7 @@
                 uidRec == null ? null : uidRec.pendingChange,
                 uid, change, procState, procStateSeq, capability, ephemeral);
         if (uidRec != null) {
-            uidRec.lastReportedChange = enqueuedChange;
+            uidRec.setLastReportedChange(enqueuedChange);
             uidRec.updateLastDispatchedProcStateSeq(enqueuedChange);
         }
 
@@ -13945,21 +14086,21 @@
         }
     }
 
-    final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
-        synchronized (mProcessStats.mLock) {
-            if (proc.thread != null) {
-                proc.mProfile.setProcessTrackerState(
-                        proc.getReportedProcState(), memFactor, now);
-            }
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    final void setProcessTrackerStateLOSP(ProcessRecord proc, int memFactor, long now) {
+        if (proc.getThread() != null) {
+            proc.mProfile.setProcessTrackerState(
+                    proc.mState.getReportedProcState(), memFactor, now);
         }
     }
 
     @GuardedBy("this")
     final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
             int fgServiceTypes, boolean oomAdj) {
-        if (isForeground != proc.hasForegroundServices()
-                || proc.getForegroundServiceTypes() != fgServiceTypes) {
-            proc.setHasForegroundServices(isForeground, fgServiceTypes);
+        final ProcessServiceRecord psr = proc.mServices;
+        if (isForeground != psr.hasForegroundServices()
+                || psr.getForegroundServiceTypes() != fgServiceTypes) {
+            psr.setHasForegroundServices(isForeground, fgServiceTypes);
             ArrayList<ProcessRecord> curProcs = mForegroundPackages.get(proc.info.packageName,
                     proc.info.uid);
             if (isForeground) {
@@ -13985,9 +14126,9 @@
                 }
             }
 
-            proc.setReportedForegroundServiceTypes(fgServiceTypes);
+            psr.setReportedForegroundServiceTypes(fgServiceTypes);
             ProcessChangeItem item = mProcessList.enqueueProcessChangeItemLocked(
-                    proc.pid, proc.info.uid);
+                    proc.getPid(), proc.info.uid);
             item.changes |= ProcessChangeItem.CHANGE_FOREGROUND_SERVICES;
             item.foregroundServiceTypes = fgServiceTypes;
         }
@@ -14030,7 +14171,6 @@
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
-
             }
         }
         return r;
@@ -14140,17 +14280,20 @@
                     final int appId = UserHandle.getAppId(pkgUid);
                     for (int i = mProcessList.mActiveUids.size() - 1; i >= 0; i--) {
                         final UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
-                        final long bgTime = uidRec.lastBackgroundTime;
-                        if (bgTime > 0 && !uidRec.idle) {
-                            if (UserHandle.getAppId(uidRec.uid) == appId) {
+                        final long bgTime = uidRec.getLastBackgroundTime();
+                        if (bgTime > 0 && !uidRec.isIdle()) {
+                            final int uid = uidRec.getUid();
+                            if (UserHandle.getAppId(uid) == appId) {
                                 if (userId == UserHandle.USER_ALL
-                                        || userId == UserHandle.getUserId(uidRec.uid)) {
-                                    EventLogTags.writeAmUidIdle(uidRec.uid);
-                                    uidRec.idle = true;
-                                    uidRec.setIdle = true;
-                                    Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uidRec.uid)
+                                        || userId == UserHandle.getUserId(uid)) {
+                                    EventLogTags.writeAmUidIdle(uid);
+                                    synchronized (mProcLock) {
+                                        uidRec.setIdle(true);
+                                        uidRec.setSetIdle(true);
+                                    }
+                                    Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uid)
                                             + " from package " + packageName + " user " + userId);
-                                    doStopUidLocked(uidRec.uid, uidRec);
+                                    doStopUidLocked(uid, uidRec);
                                 }
                             }
                         }
@@ -14175,11 +14318,11 @@
 
     final void runInBackgroundDisabled(int uid) {
         synchronized (this) {
-            UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
+            UidRecord uidRec = mProcessList.getUidRecordLOSP(uid);
             if (uidRec != null) {
                 // This uid is actually running...  should it be considered background now?
-                if (uidRec.idle) {
-                    doStopUidLocked(uidRec.uid, uidRec);
+                if (uidRec.isIdle()) {
+                    doStopUidLocked(uidRec.getUid(), uidRec);
                 }
             } else {
                 // This uid isn't actually running...  still send a report about it being "stopped".
@@ -14233,7 +14376,7 @@
                         + callerPid);
                 return;
             }
-            if (!pr.mAllowlistManager) {
+            if (!pr.mServices.mAllowlistManager) {
                 if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid)
                         != PackageManager.PERMISSION_GRANTED
                         && checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callerPid, callerUid)
@@ -14257,13 +14400,15 @@
      */
     @GuardedBy("this")
     void tempAllowlistUidLocked(int targetUid, long duration, String tag, int type) {
-        mPendingTempAllowlist.put(targetUid,
-                new PendingTempAllowlist(targetUid, duration, tag, type));
-        setUidTempAllowlistStateLocked(targetUid, true);
-        mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget();
+        synchronized (mProcLock) {
+            mPendingTempAllowlist.put(targetUid,
+                    new PendingTempAllowlist(targetUid, duration, tag, type));
+            setUidTempAllowlistStateLSP(targetUid, true);
+            mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget();
 
-        if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
-            mFgsStartTempAllowList.add(targetUid, duration);
+            if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+                mFgsStartTempAllowList.add(targetUid, duration);
+            }
         }
     }
 
@@ -14273,7 +14418,7 @@
 
         // First copy out the pending changes...  we need to leave them in the map for now,
         // in case someone needs to check what is coming up while we don't have the lock held.
-        synchronized(this) {
+        synchronized (mProcLock) {
             N = mPendingTempAllowlist.size();
             list = new PendingTempAllowlist[N];
             for (int i = 0; i < N; i++) {
@@ -14293,25 +14438,27 @@
         }
 
         // And now we can safely remove them from the map.
-        synchronized(this) {
-            for (int i = 0; i < N; i++) {
-                PendingTempAllowlist ptw = list[i];
-                int index = mPendingTempAllowlist.indexOfKey(ptw.targetUid);
-                if (index >= 0 && mPendingTempAllowlist.valueAt(index) == ptw) {
-                    mPendingTempAllowlist.removeAt(index);
+        synchronized (this) {
+            synchronized (mProcLock) {
+                for (int i = 0; i < N; i++) {
+                    PendingTempAllowlist ptw = list[i];
+                    int index = mPendingTempAllowlist.indexOfKey(ptw.targetUid);
+                    if (index >= 0 && mPendingTempAllowlist.valueAt(index) == ptw) {
+                        mPendingTempAllowlist.removeAt(index);
+                    }
                 }
             }
         }
     }
 
-    @GuardedBy("this")
-    final void setAppIdTempAllowlistStateLocked(int uid, boolean onAllowlist) {
-        mOomAdjuster.setAppIdTempAllowlistStateLocked(uid, onAllowlist);
+    @GuardedBy({"this", "mProcLock"})
+    final void setAppIdTempAllowlistStateLSP(int uid, boolean onAllowlist) {
+        mOomAdjuster.setAppIdTempAllowlistStateLSP(uid, onAllowlist);
     }
 
-    @GuardedBy("this")
-    final void setUidTempAllowlistStateLocked(int uid, boolean onAllowlist) {
-        mOomAdjuster.setUidTempAllowlistStateLocked(uid, onAllowlist);
+    @GuardedBy({"this", "mProcLock"})
+    final void setUidTempAllowlistStateLSP(int uid, boolean onAllowlist) {
+        mOomAdjuster.setUidTempAllowlistStateLSP(uid, onAllowlist);
     }
 
     private void trimApplications(boolean forceFullOomAdj, String oomAdjReason) {
@@ -14328,26 +14475,28 @@
         for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) {
             final ProcessRecord app = mProcessList.mRemovedProcesses.get(i);
             if (!app.hasActivitiesOrRecentTasks()
-                    && app.curReceivers.isEmpty() && app.numberOfRunningServices() == 0) {
-                Slog.i(
-                    TAG, "Exiting empty application process "
-                    + app.toShortString() + " ("
-                    + (app.thread != null ? app.thread.asBinder() : null)
-                    + ")\n");
-                if (app.pid > 0 && app.pid != MY_PID) {
-                    app.kill("empty",
+                    && app.mReceivers.numberOfCurReceivers() == 0
+                    && app.mServices.numberOfRunningServices() == 0) {
+                final IApplicationThread thread = app.getThread();
+                Slog.i(TAG, "Exiting empty application process "
+                        + app.toShortString() + " ("
+                        + (thread != null ? thread.asBinder() : null)
+                        + ")\n");
+                final int pid = app.getPid();
+                if (pid > 0 && pid != MY_PID) {
+                    app.killLocked("empty",
                             ApplicationExitInfo.REASON_OTHER,
                             ApplicationExitInfo.SUBREASON_TRIM_EMPTY,
                             false);
-                } else if (app.thread != null) {
+                } else if (thread != null) {
                     try {
-                        app.thread.scheduleExit();
+                        thread.scheduleExit();
                     } catch (Exception e) {
                         // Ignore exceptions.
                     }
                 }
                 didSomething = true;
-                cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
+                cleanUpApplicationRecordLocked(app, pid, false, true, -1, false /*replacingPid*/);
                 mProcessList.mRemovedProcesses.remove(i);
 
                 if (app.isPersistent()) {
@@ -14368,7 +14517,7 @@
     }
 
     /** This method sends the specified signal to each of the persistent apps */
-    public void signalPersistentProcesses(int sig) throws RemoteException {
+    public void signalPersistentProcesses(final int sig) throws RemoteException {
         if (sig != SIGNAL_USR1) {
             throw new SecurityException("Only SIGNAL_USR1 is allowed");
         }
@@ -14379,13 +14528,12 @@
                     + android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES);
         }
 
-        synchronized (this) {
-            for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                ProcessRecord r = mProcessList.mLruProcesses.get(i);
-                if (r.thread != null && r.isPersistent()) {
-                    sendSignal(r.pid, sig);
+        synchronized (mProcLock) {
+            mProcessList.forEachLruProcessesLOSP(false, app -> {
+                if (app.getThread() != null && app.isPersistent()) {
+                    sendSignal(app.getPid(), sig);
                 }
-            }
+            });
         }
     }
 
@@ -14404,21 +14552,23 @@
         }
 
         ProcessRecord proc = null;
-        synchronized (this) {
+        synchronized (mProcLock) {
             if (process != null) {
-                proc = findProcessLocked(process, userId, "profileControl");
+                proc = findProcessLOSP(process, userId, "profileControl");
             }
 
-            if (start && (proc == null || proc.thread == null)) {
+            if (start && (proc == null || proc.getThread() == null)) {
                 throw new IllegalArgumentException("Unknown process: " + process);
             }
         }
+
         synchronized (mAppProfiler.mProfilerLock) {
             return mAppProfiler.profileControlLPf(proc, start, profilerInfo, profileType);
         }
     }
 
-    private ProcessRecord findProcessLocked(String process, int userId, String callName) {
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    private ProcessRecord findProcessLOSP(String process, int userId, String callName) {
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, true, ALLOW_FULL_ONLY, callName, null);
         ProcessRecord proc = null;
@@ -14431,8 +14581,8 @@
         }
 
         if (proc == null) {
-            ArrayMap<String, SparseArray<ProcessRecord>> all
-                    = mProcessList.mProcessNames.getMap();
+            ArrayMap<String, SparseArray<ProcessRecord>> all =
+                    mProcessList.getProcessNamesLOSP().getMap();
             SparseArray<ProcessRecord> procs = all.get(process);
             if (procs != null && procs.size() > 0) {
                 proc = procs.valueAt(0);
@@ -14454,7 +14604,6 @@
     @Override
     public boolean dumpHeap(String process, int userId, boolean managed, boolean mallocInfo,
             boolean runGc, String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) {
-
         try {
             // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
             // its own permission (same as profileControl).
@@ -14468,9 +14617,10 @@
                 throw new IllegalArgumentException("null fd");
             }
 
-            synchronized (this) {
-                ProcessRecord proc = findProcessLocked(process, userId, "dumpHeap");
-                if (proc == null || proc.thread == null) {
+            synchronized (mProcLock) {
+                ProcessRecord proc = findProcessLOSP(process, userId, "dumpHeap");
+                IApplicationThread thread;
+                if (proc == null || (thread = proc.getThread()) == null) {
                     throw new IllegalArgumentException("Unknown process: " + process);
                 }
 
@@ -14492,7 +14642,7 @@
                         }
                     }, null);
 
-                proc.thread.dumpHeap(managed, mallocInfo, runGc, path, fd, intermediateCallback);
+                thread.dumpHeap(managed, mallocInfo, runGc, path, fd, intermediateCallback);
                 fd = null;
                 return true;
             }
@@ -14547,8 +14697,8 @@
     }
 
     void onCoreSettingsChange(Bundle settings) {
-        synchronized (this) {
-            mProcessList.updateCoreSettingsLocked(settings);
+        synchronized (mProcLock) {
+            mProcessList.updateCoreSettingsLOSP(settings);
         }
     }
 
@@ -14700,8 +14850,9 @@
         return info;
     }
 
-    private boolean processSanityChecksLocked(ProcessRecord process) {
-        if (process == null || process.thread == null) {
+    @GuardedBy("mProcLock")
+    private boolean processSanityChecksLPr(ProcessRecord process, IApplicationThread thread) {
+        if (process == null || thread == null) {
             return false;
         }
 
@@ -14723,34 +14874,36 @@
                     + android.Manifest.permission.SET_ACTIVITY_WATCHER);
         }
 
-        synchronized (this) {
+        synchronized (mProcLock) {
             mBinderTransactionTrackingEnabled = true;
-            for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
-                ProcessRecord process = mProcessList.mLruProcesses.get(i);
-                if (!processSanityChecksLocked(process)) {
-                    continue;
+            mProcessList.forEachLruProcessesLOSP(true, process -> {
+                final IApplicationThread thread = process.getThread();
+                if (!processSanityChecksLPr(process, thread)) {
+                    return;
                 }
                 try {
-                    process.thread.startBinderTracking();
+                    thread.startBinderTracking();
                 } catch (RemoteException e) {
                     Log.v(TAG, "Process disappared");
                 }
-            }
-            return true;
+            });
         }
+        return true;
     }
 
-    public boolean stopBinderTrackingAndDump(ParcelFileDescriptor fd) throws RemoteException {
-        try {
-            // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own
-            // permission (same as profileControl).
-            if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
-                    != PackageManager.PERMISSION_GRANTED) {
-                throw new SecurityException("Requires permission "
-                        + android.Manifest.permission.SET_ACTIVITY_WATCHER);
-            }
+    @Override
+    public boolean stopBinderTrackingAndDump(final ParcelFileDescriptor fd) throws RemoteException {
+        // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own
+        // permission (same as profileControl).
+        if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission "
+                    + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+        }
 
-            synchronized (this) {
+        boolean closeFd = true;
+        try {
+            synchronized (mProcLock) {
                 if (fd == null) {
                     throw new IllegalArgumentException("null fd");
                 }
@@ -14758,9 +14911,10 @@
 
                 PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
                 pw.println("Binder transaction traces for all processes.\n");
-                for (ProcessRecord process : mProcessList.mLruProcesses) {
-                    if (!processSanityChecksLocked(process)) {
-                        continue;
+                mProcessList.forEachLruProcessesLOSP(true, process -> {
+                    final IApplicationThread thread = process.getThread();
+                    if (!processSanityChecksLPr(process, thread)) {
+                        return;
                     }
 
                     pw.println("Traces for process: " + process.processName);
@@ -14768,7 +14922,7 @@
                     try {
                         TransferPipe tp = new TransferPipe();
                         try {
-                            process.thread.stopBinderTrackingAndDump(tp.getWriteFd());
+                            thread.stopBinderTrackingAndDump(tp.getWriteFd());
                             tp.go(fd.getFileDescriptor());
                         } finally {
                             tp.kill();
@@ -14782,12 +14936,12 @@
                                 process + ".  Exception: " + e);
                         pw.flush();
                     }
-                }
-                fd = null;
+                });
+                closeFd = false;
                 return true;
             }
         } finally {
-            if (fd != null) {
+            if (fd != null && closeFd) {
                 try {
                     fd.close();
                 } catch (IOException e) {
@@ -14832,12 +14986,12 @@
 
         @Override
         public void killForegroundAppsForUser(@UserIdInt int userId) {
-            synchronized (ActivityManagerService.this) {
-                final ArrayList<ProcessRecord> procs = new ArrayList<>();
-                final int NP = mProcessList.mProcessNames.getMap().size();
-                for (int ip = 0; ip < NP; ip++) {
+            final ArrayList<ProcessRecord> procs = new ArrayList<>();
+            synchronized (mProcLock) {
+                final int numOfProcs = mProcessList.getProcessNamesLOSP().getMap().size();
+                for (int ip = 0; ip < numOfProcs; ip++) {
                     final SparseArray<ProcessRecord> apps =
-                            mProcessList.mProcessNames.getMap().valueAt(ip);
+                            mProcessList.getProcessNamesLOSP().getMap().valueAt(ip);
                     final int NA = apps.size();
                     for (int ia = 0; ia < NA; ia++) {
                         final ProcessRecord app = apps.valueAt(ia);
@@ -14845,19 +14999,23 @@
                             // We don't kill persistent processes.
                             continue;
                         }
-                        if (app.removed
-                                || (app.userId == userId && app.hasForegroundActivities())) {
+                        if (app.isRemoved()
+                                || (app.userId == userId && app.mState.hasForegroundActivities())) {
                             procs.add(app);
                         }
                     }
                 }
+            }
 
-                final int N = procs.size();
-                for (int i = 0; i < N; i++) {
-                    mProcessList.removeProcessLocked(procs.get(i), false, true,
-                            ApplicationExitInfo.REASON_OTHER,
-                            ApplicationExitInfo.SUBREASON_KILL_ALL_FG,
-                            "kill all fg");
+            final int numOfProcs = procs.size();
+            if (numOfProcs > 0) {
+                synchronized (ActivityManagerService.this) {
+                    for (int i = 0; i < numOfProcs; i++) {
+                        mProcessList.removeProcessLocked(procs.get(i), false, true,
+                                ApplicationExitInfo.REASON_OTHER,
+                                ApplicationExitInfo.SUBREASON_KILL_ALL_FG,
+                                "kill all fg");
+                    }
                 }
             }
         }
@@ -14903,22 +15061,26 @@
         @Override
         public void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids) {
             synchronized (ActivityManagerService.this) {
-                mDeviceIdleAllowlist = allAppids;
-                mDeviceIdleExceptIdleAllowlist = exceptIdleAppids;
+                synchronized (mProcLock) {
+                    mDeviceIdleAllowlist = allAppids;
+                    mDeviceIdleExceptIdleAllowlist = exceptIdleAppids;
+                }
             }
         }
 
         @Override
         public void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, boolean adding,
-                long durationMs, @BroadcastOptions.TempAllowListType int type) {
+                long durationMs, @TempAllowListType int type) {
             synchronized (ActivityManagerService.this) {
-                mDeviceIdleTempAllowlist = appids;
-                if (adding) {
-                    if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
-                        mFgsStartTempAllowList.add(changingUid, durationMs);
+                synchronized (mProcLock) {
+                    mDeviceIdleTempAllowlist = appids;
+                    if (adding) {
+                        if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+                            mFgsStartTempAllowList.add(changingUid, durationMs);
+                        }
                     }
+                    setAppIdTempAllowlistStateLSP(changingUid, adding);
                 }
-                setAppIdTempAllowlistStateLocked(changingUid, adding);
             }
         }
 
@@ -14956,10 +15118,10 @@
                         return;
                     }
                 }
-                if (pr.hasOverlayUi() == hasOverlayUi) {
+                if (pr.mState.hasOverlayUi() == hasOverlayUi) {
                     return;
                 }
-                pr.setHasOverlayUi(hasOverlayUi);
+                pr.mState.setHasOverlayUi(hasOverlayUi);
                 //Slog.i(TAG, "Setting hasOverlayUi=" + pr.hasOverlayUi + " for pid=" + pid);
                 updateOomAdjLocked(pr, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
             }
@@ -14977,8 +15139,8 @@
                         + uid + " seq: " + procStateSeq);
             }
             UidRecord record;
-            synchronized (ActivityManagerService.this) {
-                record = mProcessList.getUidRecordLocked(uid);
+            synchronized (mProcLock) {
+                record = mProcessList.getUidRecordLOSP(uid);
                 if (record == null) {
                     if (DEBUG_NETWORK) {
                         Slog.d(TAG_NETWORK, "No active uidRecord for uid: " + uid
@@ -15041,19 +15203,21 @@
 
         @Override
         public boolean isUidActive(int uid) {
-            synchronized (ActivityManagerService.this) {
-                return isUidActiveLocked(uid);
+            synchronized (mProcLock) {
+                return isUidActiveLOSP(uid);
             }
         }
 
         @Override
         public List<ProcessMemoryState> getMemoryStateForProcesses() {
             List<ProcessMemoryState> processMemoryStates = new ArrayList<>();
-            synchronized (mPidsSelfLocked) {
-                for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) {
-                    final ProcessRecord r = mPidsSelfLocked.valueAt(i);
-                    processMemoryStates.add(
-                            new ProcessMemoryState(r.uid, r.pid, r.processName, r.curAdj));
+            synchronized (mProcLock) {
+                synchronized (mPidsSelfLocked) {
+                    for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) {
+                        final ProcessRecord r = mPidsSelfLocked.valueAt(i);
+                        processMemoryStates.add(new ProcessMemoryState(
+                                r.uid, r.getPid(), r.processName, r.mState.getCurAdj()));
+                    }
                 }
             }
             return processMemoryStates;
@@ -15093,14 +15257,14 @@
                     final WindowProcessController wpc =
                             (WindowProcessController) procsToKill.get(i);
                     final ProcessRecord pr = (ProcessRecord) wpc.mOwner;
-                    if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                            && pr.curReceivers.isEmpty()) {
-                        pr.kill("remove task", ApplicationExitInfo.REASON_USER_REQUESTED,
+                    if (pr.mState.getSetSchedGroup() == ProcessList.SCHED_GROUP_BACKGROUND
+                            && pr.mReceivers.numberOfCurReceivers() == 0) {
+                        pr.killLocked("remove task", ApplicationExitInfo.REASON_USER_REQUESTED,
                                 ApplicationExitInfo.SUBREASON_UNKNOWN, true);
                     } else {
                         // We delay killing processes that are not in the background or running a
                         // receiver.
-                        pr.waitingToKill = "remove task";
+                        pr.setWaitingToKill("remove task");
                     }
                 }
             }
@@ -15122,18 +15286,15 @@
         public boolean hasRunningActivity(int uid, @Nullable String packageName) {
             if (packageName == null) return false;
 
-            synchronized (ActivityManagerService.this) {
-                for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
-                    final ProcessRecord pr = mProcessList.mLruProcesses.get(i);
-                    if (pr.uid != uid) {
-                        continue;
+            synchronized (mProcLock) {
+                return mProcessList.searchEachLruProcessesLOSP(true, app -> {
+                    if (app.uid == uid
+                            && app.getWindowProcessController().hasRunningActivity(packageName)) {
+                        return Boolean.TRUE;
                     }
-                    if (pr.getWindowProcessController().hasRunningActivity(packageName)) {
-                        return true;
-                    }
-                }
+                    return null;
+                }) != null;
             }
-            return false;
         }
 
         @Override
@@ -15564,7 +15725,7 @@
             }
             synchronized (mPidsSelfLocked) {
                 final ProcessRecord pr = mPidsSelfLocked.get(pid);
-                return pr == null ? Zygote.MOUNT_EXTERNAL_NONE : pr.mountMode;
+                return pr == null ? Zygote.MOUNT_EXTERNAL_NONE : pr.getMountMode();
             }
         }
 
@@ -15595,19 +15756,17 @@
         @Override
         public boolean hasRunningForegroundService(int uid, int foregroundServicetype) {
             synchronized (ActivityManagerService.this) {
-                for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
-                    final ProcessRecord pr = mProcessList.mLruProcesses.get(i);
-                    if (pr.uid != uid) {
-                        continue;
+                return mProcessList.searchEachLruProcessesLOSP(true, app -> {
+                    if (app.uid != uid) {
+                        return null;
                     }
 
-                    if ((pr.getForegroundServiceTypes() & foregroundServicetype) != 0) {
-                        return true;
+                    if ((app.mServices.getForegroundServiceTypes() & foregroundServicetype) != 0) {
+                        return Boolean.TRUE;
                     }
-                }
+                    return null;
+                }) != null;
             }
-
-            return false;
         }
 
         @Override
@@ -15622,7 +15781,7 @@
 
         @Override
         public boolean isUidCurrentlyInstrumented(int uid) {
-            synchronized (ActivityManagerService.this) {
+            synchronized (mProcLock) {
                 for (int i = mActiveInstrumentation.size() - 1; i >= 0; i--) {
                     ActiveInstrumentation activeInst = mActiveInstrumentation.get(i);
                     if (!activeInst.mFinished && activeInst.mTargetInfo != null
@@ -15774,8 +15933,8 @@
             Slog.d(TAG_NETWORK, "Called from " + callingUid + " to wait for seq: " + procStateSeq);
         }
         UidRecord record;
-        synchronized (this) {
-            record = mProcessList.getUidRecordLocked(callingUid);
+        synchronized (mProcLock) {
+            record = mProcessList.getUidRecordLOSP(callingUid);
             if (record == null) {
                 return;
             }
@@ -15891,11 +16050,13 @@
         }
         try {
             synchronized(this) {
-                mProcessList.killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid),
-                        userId, ProcessList.FOREGROUND_APP_ADJ,
-                        ApplicationExitInfo.REASON_DEPENDENCY_DIED,
-                        ApplicationExitInfo.SUBREASON_UNKNOWN,
-                        "dep: " + packageName);
+                synchronized (mProcLock) {
+                    mProcessList.killPackageProcessesLSP(packageName, UserHandle.getAppId(pkgUid),
+                            userId, ProcessList.FOREGROUND_APP_ADJ,
+                            ApplicationExitInfo.REASON_DEPENDENCY_DIED,
+                            ApplicationExitInfo.SUBREASON_UNKNOWN,
+                            "dep: " + packageName);
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -15912,10 +16073,10 @@
         enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
                 "scheduleApplicationInfoChanged()");
 
-        synchronized (this) {
+        synchronized (mProcLock) {
             final long origId = Binder.clearCallingIdentity();
             try {
-                updateApplicationInfoLocked(packageNames, userId);
+                updateApplicationInfoLOSP(packageNames, userId);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -15934,12 +16095,13 @@
         ActivityThread.currentActivityThread().handleSystemApplicationInfoChanged(ai);
     }
 
-    void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) {
+    @GuardedBy(anyOf = {"this", "mProcLock"})
+    private void updateApplicationInfoLOSP(@NonNull List<String> packagesToUpdate, int userId) {
         final boolean updateFrameworkRes = packagesToUpdate.contains("android");
         if (updateFrameworkRes) {
             PackageParser.readConfigUseRoundIcon(null);
         }
-        mProcessList.updateApplicationInfoLocked(packagesToUpdate, userId, updateFrameworkRes);
+        mProcessList.updateApplicationInfoLOSP(packagesToUpdate, userId, updateFrameworkRes);
 
         if (updateFrameworkRes) {
             // Update system server components that need to know about changed overlays. Because the
@@ -15968,7 +16130,7 @@
             final int batchSize;
             final float threshold;
             final BinderCallHeavyHitterListener listener;
-            synchronized (ActivityManagerService.this) {
+            synchronized (mProcLock) {
                 if (mConstants.BINDER_HEAVY_HITTER_WATCHER_ENABLED) {
                     // Default watcher takes precedence, ignore the auto sampler.
                     mHandler.removeMessages(BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG);
@@ -16005,7 +16167,7 @@
             final int batchSize;
             final float threshold;
             final long now;
-            synchronized (ActivityManagerService.this) {
+            synchronized (mProcLock) {
                 if (!mConstants.BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED) {
                     // It's configured OFF
                     return;
@@ -16039,7 +16201,7 @@
      * Stop the binder heavy hitter auto sampler after given timeout.
      */
     private void handleBinderHeavyHitterAutoSamplerTimeOut() {
-        synchronized (ActivityManagerService.this) {
+        synchronized (mProcLock) {
             if (mConstants.BINDER_HEAVY_HITTER_WATCHER_ENABLED) {
                 // The default watcher is ON, don't bother to stop it.
                 return;
@@ -16085,10 +16247,11 @@
      */
     public void attachAgent(String process, String path) {
         try {
-            synchronized (this) {
-                ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM,
+            synchronized (mProcLock) {
+                ProcessRecord proc = findProcessLOSP(process, UserHandle.USER_SYSTEM,
                         "attachAgent");
-                if (proc == null || proc.thread == null) {
+                IApplicationThread thread;
+                if (proc == null || (thread = proc.getThread()) == null) {
                     throw new IllegalArgumentException("Unknown process: " + process);
                 }
 
@@ -16098,7 +16261,7 @@
                     }
                 }
 
-                proc.thread.attachAgent(path);
+                thread.attachAgent(path);
             }
         } catch (RemoteException e) {
             throw new IllegalStateException("Process disappeared");
@@ -16167,7 +16330,7 @@
         }
 
         // We allow delegation only to one instrumentation started from the shell
-        synchronized (ActivityManagerService.this) {
+        synchronized (mProcLock) {
             // If the delegate is already set up for the target UID, nothing to do.
             if (mAppOpsService.getAppOpsServiceDelegate() != null) {
                 if (!(mAppOpsService.getAppOpsServiceDelegate() instanceof ShellDelegate)) {
@@ -16213,7 +16376,7 @@
                 && UserHandle.getCallingAppId() != Process.ROOT_UID) {
             throw new SecurityException("Only the shell can delegate its permissions");
         }
-        synchronized (ActivityManagerService.this) {
+        synchronized (mProcLock) {
             mAppOpsService.setAppOpsServiceDelegate(null);
             getPermissionManagerInternal().stopShellPermissionIdentityDelegation();
         }
@@ -16335,7 +16498,7 @@
         if (!isCallerShell()) {
             throw new SecurityException("Only shell can call it");
         }
-        synchronized (this) {
+        synchronized (mProcLock) {
             try {
                 if (mLifeMonitorFds == null) {
                     mLifeMonitorFds = ParcelFileDescriptor.createPipe();
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index a7119d1..34d9a60 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -160,7 +160,7 @@
         }
 
         void appNotResponding(boolean onlyDumpSelf) {
-            mApp.appNotResponding(mActivityShortComponentName, mAppInfo,
+            mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,
                     mParentShortComponentName, mParentProcess, mAboveSystem, mAnnotation,
                     onlyDumpSelf);
         }
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index 779d378..48222cb 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -38,6 +38,7 @@
 final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListener {
 
     private final ActivityManagerService mService;
+    private final ActivityManagerGlobalLock mProcLock;
     private final AppErrorResult mResult;
     private final ProcessRecord mProc;
     private final boolean mIsRestartable;
@@ -63,6 +64,7 @@
         Resources res = context.getResources();
 
         mService = service;
+        mProcLock = service.mProcLock;
         mProc = data.proc;
         mResult = data.result;
         mIsRestartable = (data.taskId != INVALID_TASK_ID || data.isRestartableForService)
@@ -71,7 +73,7 @@
         BidiFormatter bidi = BidiFormatter.getInstance();
 
         CharSequence name;
-        if ((mProc.getPkgList().size() == 1)
+        if (mProc.getPkgList().size() == 1
                 && (name = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
             setTitle(res.getString(
                     data.repeating ? com.android.internal.R.string.aerr_application_repeated
@@ -112,7 +114,7 @@
         LayoutInflater.from(context).inflate(
                 com.android.internal.R.layout.app_error_dialog, frame, true);
 
-        final boolean hasReceiver = mProc.errorReportReceiver != null;
+        final boolean hasReceiver = mProc.mErrorState.getErrorReportReceiver() != null;
 
         final TextView restart = findViewById(com.android.internal.R.id.aerr_restart);
         restart.setOnClickListener(this);
@@ -166,11 +168,11 @@
     }
 
     private void setResult(int result) {
-        synchronized (mService) {
+        synchronized (mProcLock) {
             if (mProc != null) {
                 // Don't dismiss again since it leads to recursive call between dismiss and this
                 // method.
-                mProc.getDialogController().clearCrashDialogs(false /* needDismiss */);
+                mProc.mErrorState.getDialogController().clearCrashDialogs(false /* needDismiss */);
             }
         }
         mResult.set(result);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 7be54c2..e5a5cff 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -72,6 +72,7 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "AppErrors" : TAG_AM;
 
     private final ActivityManagerService mService;
+    private final ActivityManagerGlobalLock mProcLock;
     private final Context mContext;
     private final PackageWatchdog mPackageWatchdog;
 
@@ -137,6 +138,7 @@
     AppErrors(Context context, ActivityManagerService service, PackageWatchdog watchdog) {
         context.assertRuntimeOverlayThemable();
         mService = service;
+        mProcLock = service.mProcLock;
         mContext = context;
         mPackageWatchdog = watchdog;
     }
@@ -154,17 +156,48 @@
         }
     }
 
-    void dumpDebug(ProtoOutputStream proto, long fieldId, String dumpPackage) {
-        synchronized (mBadProcessLock) {
-            final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses;
-            if (mProcessCrashTimes.getMap().isEmpty() && badProcesses.getMap().isEmpty()) {
-                return;
+    @GuardedBy("mProcLock")
+    void dumpDebugLPr(ProtoOutputStream proto, long fieldId, String dumpPackage) {
+        final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses;
+        if (mProcessCrashTimes.getMap().isEmpty() && badProcesses.getMap().isEmpty()) {
+            return;
+        }
+
+        final long token = proto.start(fieldId);
+        final long now = SystemClock.uptimeMillis();
+        proto.write(AppErrorsProto.NOW_UPTIME_MS, now);
+
+        if (!badProcesses.getMap().isEmpty()) {
+            final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = badProcesses.getMap();
+            final int processCount = pmap.size();
+            for (int ip = 0; ip < processCount; ip++) {
+                final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES);
+                final String pname = pmap.keyAt(ip);
+                final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
+                final int uidCount = uids.size();
+
+                proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
+                for (int i = 0; i < uidCount; i++) {
+                    final int puid = uids.keyAt(i);
+                    final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
+                    if (dumpPackage != null && (r == null
+                            || !r.getPkgList().containsKey(dumpPackage))) {
+                        continue;
+                    }
+                    final BadProcessInfo info = uids.valueAt(i);
+                    final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES);
+                    proto.write(AppErrorsProto.BadProcess.Entry.UID, puid);
+                    proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time);
+                    proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg);
+                    proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg);
+                    proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack);
+                    proto.end(etoken);
+                }
+                proto.end(btoken);
             }
+        }
 
-            final long token = proto.start(fieldId);
-            final long now = SystemClock.uptimeMillis();
-            proto.write(AppErrorsProto.NOW_UPTIME_MS, now);
-
+        synchronized (mBadProcessLock) {
             if (!mProcessCrashTimes.getMap().isEmpty()) {
                 final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
                 final int procCount = pmap.size();
@@ -177,7 +210,7 @@
                     proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname);
                     for (int i = 0; i < uidCount; i++) {
                         final int puid = uids.keyAt(i);
-                        final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+                        final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
                         if (dumpPackage != null
                                 && (r == null || !r.getPkgList().containsKey(dumpPackage))) {
                             continue;
@@ -190,44 +223,14 @@
                     }
                     proto.end(ctoken);
                 }
-
             }
-
-            if (!badProcesses.getMap().isEmpty()) {
-                final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = badProcesses.getMap();
-                final int processCount = pmap.size();
-                for (int ip = 0; ip < processCount; ip++) {
-                    final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES);
-                    final String pname = pmap.keyAt(ip);
-                    final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
-                    final int uidCount = uids.size();
-
-                    proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
-                    for (int i = 0; i < uidCount; i++) {
-                        final int puid = uids.keyAt(i);
-                        final ProcessRecord r = mService.getProcessNames().get(pname, puid);
-                        if (dumpPackage != null && (r == null
-                                    || !r.getPkgList().containsKey(dumpPackage))) {
-                            continue;
-                        }
-                        final BadProcessInfo info = uids.valueAt(i);
-                        final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES);
-                        proto.write(AppErrorsProto.BadProcess.Entry.UID, puid);
-                        proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time);
-                        proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg);
-                        proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg);
-                        proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack);
-                        proto.end(etoken);
-                    }
-                    proto.end(btoken);
-                }
-            }
-
-            proto.end(token);
         }
+
+        proto.end(token);
     }
 
-    boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String dumpPackage) {
+    @GuardedBy("mProcLock")
+    boolean dumpLPr(FileDescriptor fd, PrintWriter pw, boolean needSep, String dumpPackage) {
         final long now = SystemClock.uptimeMillis();
         synchronized (mBadProcessLock) {
             if (!mProcessCrashTimes.getMap().isEmpty()) {
@@ -240,9 +243,9 @@
                     final int uidCount = uids.size();
                     for (int i = 0; i < uidCount; i++) {
                         final int puid = uids.keyAt(i);
-                        final ProcessRecord r = mService.getProcessNames().get(pname, puid);
-                        if (dumpPackage != null && (r == null
-                                    || !r.getPkgList().containsKey(dumpPackage))) {
+                        final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
+                        if (dumpPackage != null
+                                && (r == null || !r.getPkgList().containsKey(dumpPackage))) {
                             continue;
                         }
                         if (!printed) {
@@ -271,7 +274,7 @@
                     final int uidCount = uids.size();
                     for (int i = 0; i < uidCount; i++) {
                         final int puid = uids.keyAt(i);
-                        final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+                        final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
                         if (dumpPackage != null
                                 && (r == null || !r.getPkgList().containsKey(dumpPackage))) {
                             continue;
@@ -303,7 +306,7 @@
                 final int uidCount = uids.size();
                 for (int i = 0; i < uidCount; i++) {
                     final int puid = uids.keyAt(i);
-                    final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+                    final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
                     if (dumpPackage != null && (r == null
                             || !r.getPkgList().containsKey(dumpPackage))) {
                         continue;
@@ -330,14 +333,14 @@
                         for (int pos = 0; pos < info.stack.length(); pos++) {
                             if (info.stack.charAt(pos) == '\n') {
                                 pw.print("        ");
-                                pw.write(info.stack, lastPos, pos-lastPos);
+                                pw.write(info.stack, lastPos, pos - lastPos);
                                 pw.println();
-                                lastPos = pos+1;
+                                lastPos = pos + 1;
                             }
                         }
                         if (lastPos < info.stack.length()) {
                             pw.print("        ");
-                            pw.write(info.stack, lastPos, info.stack.length()-lastPos);
+                            pw.write(info.stack, lastPos, info.stack.length() - lastPos);
                             pw.println();
                         }
                     }
@@ -437,32 +440,38 @@
         }
     }
 
+    @GuardedBy("mService")
     void killAppAtUserRequestLocked(ProcessRecord app) {
-        ProcessRecord.ErrorDialogController controller =
-                app.getDialogController();
+        ErrorDialogController controller = app.mErrorState.getDialogController();
 
         int reasonCode = ApplicationExitInfo.REASON_ANR;
         int subReason = ApplicationExitInfo.SUBREASON_UNKNOWN;
-        if (controller.hasDebugWaitingDialog()) {
-            reasonCode = ApplicationExitInfo.REASON_OTHER;
-            subReason = ApplicationExitInfo.SUBREASON_WAIT_FOR_DEBUGGER;
+        synchronized (mProcLock) {
+            if (controller.hasDebugWaitingDialog()) {
+                reasonCode = ApplicationExitInfo.REASON_OTHER;
+                subReason = ApplicationExitInfo.SUBREASON_WAIT_FOR_DEBUGGER;
+            }
+            controller.clearAllErrorDialogs();
+            killAppImmediateLSP(app, reasonCode, subReason,
+                    "user-terminated", "user request after error");
         }
-
-        controller.clearAllErrorDialogs();
-        killAppImmediateLocked(app, reasonCode, subReason,
-                "user-terminated", "user request after error");
     }
 
-    private void killAppImmediateLocked(ProcessRecord app, int reasonCode, int subReason,
+    @GuardedBy({"mService", "mProcLock"})
+    private void killAppImmediateLSP(ProcessRecord app, int reasonCode, int subReason,
             String reason, String killReason) {
-        app.setCrashing(false);
-        app.crashingReport = null;
-        app.setNotResponding(false);
-        app.notRespondingReport = null;
-        if (app.pid > 0 && app.pid != MY_PID) {
-            handleAppCrashLocked(app, reason,
-                    null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/);
-            app.kill(killReason, reasonCode, subReason, true);
+        final ProcessErrorStateRecord errState = app.mErrorState;
+        errState.setCrashing(false);
+        errState.setCrashingReport(null);
+        errState.setNotResponding(false);
+        errState.setNotRespondingReport(null);
+        final int pid = errState.mApp.getPid();
+        if (pid > 0 && pid != MY_PID) {
+            synchronized (mBadProcessLock) {
+                handleAppCrashLSPB(app, reason,
+                        null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/);
+            }
+            app.killLocked(killReason, reasonCode, subReason, true);
         }
     }
 
@@ -489,7 +498,7 @@
                 if (uid >= 0 && p.uid != uid) {
                     continue;
                 }
-                if (p.pid == initialPid) {
+                if (p.getPid() == initialPid) {
                     proc = p;
                     break;
                 }
@@ -508,7 +517,7 @@
             return;
         }
 
-        proc.scheduleCrash(message);
+        proc.scheduleCrashLocked(message);
         if (force) {
             // If the app is responsive, the scheduled crash will happen as expected
             // and then the delayed summary kill will be a no-op.
@@ -516,9 +525,11 @@
             mService.mHandler.postDelayed(
                     () -> {
                         synchronized (mService) {
-                            killAppImmediateLocked(p, ApplicationExitInfo.REASON_OTHER,
-                                    ApplicationExitInfo.SUBREASON_INVALID_STATE,
-                                    "forced", "killed for invalid state");
+                            synchronized (mProcLock) {
+                                killAppImmediateLSP(p, ApplicationExitInfo.REASON_OTHER,
+                                        ApplicationExitInfo.SUBREASON_INVALID_STATE,
+                                        "forced", "killed for invalid state");
+                            }
                         }
                     },
                     5000L);
@@ -627,52 +638,54 @@
         if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
             res = AppErrorDialog.FORCE_QUIT;
         }
-        if (res == AppErrorDialog.MUTE) {
-            synchronized (mBadProcessLock) {
-                stopReportingCrashesLBp(r);
-            }
-        }
-        if (res == AppErrorDialog.RESTART) {
-            synchronized (mService) {
-                mService.mProcessList.removeProcessLocked(r, false, true,
-                        ApplicationExitInfo.REASON_CRASH, "crash");
-            }
-            if (taskId != INVALID_TASK_ID) {
-                try {
-                    mService.startActivityFromRecents(taskId,
-                            ActivityOptions.makeBasic().toBundle());
-                } catch (IllegalArgumentException e) {
-                    // Hmm...that didn't work. Task should either be in recents or associated
-                    // with a stack.
-                    Slog.e(TAG, "Could not restart taskId=" + taskId, e);
+        switch (res) {
+            case AppErrorDialog.MUTE:
+                synchronized (mBadProcessLock) {
+                    stopReportingCrashesLBp(r);
                 }
-            }
-        }
-        if (res == AppErrorDialog.FORCE_QUIT) {
-            final long orig = Binder.clearCallingIdentity();
-            try {
-                // Kill it with fire!
-                mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
-                if (!r.isPersistent()) {
-                    synchronized (mService) {
-                        mService.mProcessList.removeProcessLocked(r, false, false,
-                                ApplicationExitInfo.REASON_CRASH, "crash");
+                break;
+            case AppErrorDialog.RESTART:
+                synchronized (mService) {
+                    mService.mProcessList.removeProcessLocked(r, false, true,
+                            ApplicationExitInfo.REASON_CRASH, "crash");
+                }
+                if (taskId != INVALID_TASK_ID) {
+                    try {
+                        mService.startActivityFromRecents(taskId,
+                                ActivityOptions.makeBasic().toBundle());
+                    } catch (IllegalArgumentException e) {
+                        // Hmm...that didn't work. Task should either be in recents or associated
+                        // with a stack.
+                        Slog.e(TAG, "Could not restart taskId=" + taskId, e);
                     }
-                    mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
                 }
-            } finally {
-                Binder.restoreCallingIdentity(orig);
-            }
-        }
-        if (res == AppErrorDialog.APP_INFO) {
-            appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
-            appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
-            appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        }
-        if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
-            synchronized (mService) {
-                appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
-            }
+                break;
+            case AppErrorDialog.FORCE_QUIT:
+                final long orig = Binder.clearCallingIdentity();
+                try {
+                    // Kill it with fire!
+                    mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
+                    if (!r.isPersistent()) {
+                        synchronized (mService) {
+                            mService.mProcessList.removeProcessLocked(r, false, false,
+                                    ApplicationExitInfo.REASON_CRASH, "crash");
+                        }
+                        mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(orig);
+                }
+                break;
+            case AppErrorDialog.APP_INFO:
+                appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+                appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
+                appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                break;
+            case AppErrorDialog.FORCE_QUIT_AND_REPORT:
+                synchronized (mProcLock) {
+                    appErrorIntent = createAppErrorIntentLOSP(r, timeMillis, crashInfo);
+                }
+                break;
         }
 
         if (appErrorIntent != null) {
@@ -684,13 +697,14 @@
         }
     }
 
+    @GuardedBy("mService")
     private boolean handleAppCrashInActivityController(ProcessRecord r,
                                                        ApplicationErrorReport.CrashInfo crashInfo,
                                                        String shortMsg, String longMsg,
                                                        String stackTrace, long timeMillis,
                                                        int callingPid, int callingUid) {
         String name = r != null ? r.processName : null;
-        int pid = r != null ? r.pid : callingPid;
+        int pid = r != null ? r.getPid() : callingPid;
         int uid = r != null ? r.info.uid : callingUid;
 
         return mService.mAtmInternal.handleAppCrashInActivityController(
@@ -703,7 +717,7 @@
                     Slog.w(TAG, "Force-killing crashed app " + name + " at watcher's request");
                     if (r != null) {
                         if (!makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, null)) {
-                            r.kill("crash", ApplicationExitInfo.REASON_CRASH, true);
+                            r.killLocked("crash", ApplicationExitInfo.REASON_CRASH, true);
                         }
                     } else {
                         // Huh.
@@ -718,15 +732,22 @@
         });
     }
 
+    @GuardedBy("mService")
     private boolean makeAppCrashingLocked(ProcessRecord app,
             String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
-        app.setCrashing(true);
-        app.crashingReport = generateProcessError(app,
-                ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
-        app.startAppProblemLocked();
-        app.getWindowProcessController().stopFreezingActivities();
-        return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
-                data);
+        synchronized (mProcLock) {
+            final ProcessErrorStateRecord errState = app.mErrorState;
+            errState.setCrashing(true);
+            errState.setCrashingReport(generateProcessError(app,
+                    ActivityManager.ProcessErrorStateInfo.CRASHED,
+                    null, shortMsg, longMsg, stackTrace));
+            errState.startAppProblemLSP();
+            app.getWindowProcessController().stopFreezingActivities();
+            synchronized (mBadProcessLock) {
+                return handleAppCrashLSPB(app, "force-crash" /*reason*/, shortMsg, longMsg,
+                        stackTrace, data);
+            }
+        }
     }
 
     /**
@@ -748,7 +769,7 @@
 
         report.condition = condition;
         report.processName = app.processName;
-        report.pid = app.pid;
+        report.pid = app.getPid();
         report.uid = app.info.uid;
         report.tag = activity;
         report.shortMsg = shortMsg;
@@ -758,170 +779,157 @@
         return report;
     }
 
-    Intent createAppErrorIntentLocked(ProcessRecord r,
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    Intent createAppErrorIntentLOSP(ProcessRecord r,
             long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
-        ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo);
+        ApplicationErrorReport report = createAppErrorReportLOSP(r, timeMillis, crashInfo);
         if (report == null) {
             return null;
         }
         Intent result = new Intent(Intent.ACTION_APP_ERROR);
-        result.setComponent(r.errorReportReceiver);
+        result.setComponent(r.mErrorState.getErrorReportReceiver());
         result.putExtra(Intent.EXTRA_BUG_REPORT, report);
         result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         return result;
     }
 
-    private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r,
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    private ApplicationErrorReport createAppErrorReportLOSP(ProcessRecord r,
             long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
-        if (r.errorReportReceiver == null) {
+        final ProcessErrorStateRecord errState = r.mErrorState;
+        if (errState.getErrorReportReceiver() == null) {
             return null;
         }
 
-        if (!r.isCrashing() && !r.isNotResponding() && !r.forceCrashReport) {
+        if (!errState.isCrashing() && !errState.isNotResponding()
+                && !errState.isForceCrashReport()) {
             return null;
         }
 
         ApplicationErrorReport report = new ApplicationErrorReport();
         report.packageName = r.info.packageName;
-        report.installerPackageName = r.errorReportReceiver.getPackageName();
+        report.installerPackageName = errState.getErrorReportReceiver().getPackageName();
         report.processName = r.processName;
         report.time = timeMillis;
         report.systemApp = (r.info.flags & FLAG_SYSTEM) != 0;
 
-        if (r.isCrashing() || r.forceCrashReport) {
+        if (errState.isCrashing() || errState.isForceCrashReport()) {
             report.type = ApplicationErrorReport.TYPE_CRASH;
             report.crashInfo = crashInfo;
-        } else if (r.isNotResponding()) {
+        } else if (errState.isNotResponding()) {
             report.type = ApplicationErrorReport.TYPE_ANR;
             report.anrInfo = new ApplicationErrorReport.AnrInfo();
 
-            report.anrInfo.activity = r.notRespondingReport.tag;
-            report.anrInfo.cause = r.notRespondingReport.shortMsg;
-            report.anrInfo.info = r.notRespondingReport.longMsg;
+            report.anrInfo.activity = errState.getNotRespondingReport().tag;
+            report.anrInfo.cause = errState.getNotRespondingReport().shortMsg;
+            report.anrInfo.info = errState.getNotRespondingReport().longMsg;
         }
 
         return report;
     }
 
-    private boolean handleAppCrashLocked(ProcessRecord app, String reason,
+    @GuardedBy({"mService", "mProcLock", "mBadProcessLock"})
+    private boolean handleAppCrashLSPB(ProcessRecord app, String reason,
             String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
         final long now = SystemClock.uptimeMillis();
         final boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                 Settings.Secure.ANR_SHOW_BACKGROUND, 0,
                 mService.mUserController.getCurrentUserId()) != 0;
 
-        final boolean procIsBoundForeground =
-            (app.getCurProcState() == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-
         Long crashTime;
         Long crashTimePersistent;
-        boolean tryAgain = false;
+        final String processName = app.processName;
+        final int uid = app.uid;
+        final int userId = app.userId;
+        final boolean isolated = app.isolated;
+        final boolean persistent = app.isPersistent();
+        final WindowProcessController proc = app.getWindowProcessController();
+        final ProcessErrorStateRecord errState = app.mErrorState;
 
-        synchronized (mBadProcessLock) {
-            if (!app.isolated) {
-                crashTime = mProcessCrashTimes.get(app.processName, app.uid);
-                crashTimePersistent = mProcessCrashTimesPersistent.get(app.processName, app.uid);
-            } else {
-                crashTime = crashTimePersistent = null;
-            }
+        if (!app.isolated) {
+            crashTime = mProcessCrashTimes.get(processName, uid);
+            crashTimePersistent = mProcessCrashTimesPersistent.get(processName, uid);
+        } else {
+            crashTime = crashTimePersistent = null;
+        }
 
-            // Bump up the crash count of any services currently running in the proc.
-            for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
-                // Any services running in the application need to be placed
-                // back in the pending list.
-                ServiceRecord sr = app.getRunningServiceAt(i);
-                // If the service was restarted a while ago, then reset crash count,
-                // else increment it.
-                if (now > sr.restartTime + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
-                    sr.crashCount = 1;
-                } else {
-                    sr.crashCount++;
-                }
-                // Allow restarting for started or bound foreground services that are crashing.
-                // This includes wallpapers.
-                if (sr.crashCount < mService.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY
-                        && (sr.isForeground || procIsBoundForeground)) {
-                    tryAgain = true;
-                }
-            }
+        // Bump up the crash count of any services currently running in the proc.
+        boolean tryAgain = app.mServices.incServiceCrashCountLocked(now);
 
-            final boolean quickCrash = crashTime != null
-                    && now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
-            if (quickCrash || isProcOverCrashLimitLBp(app, now)) {
-                // The process either crashed again very quickly or has been crashing periodically
-                // in the last few hours. If it was a bound foreground service, let's try to
-                // restart again in a while, otherwise the process loses!
-                Slog.w(TAG, "Process " + app.processName + " has crashed too many times, killing!"
-                        + " Reason: "
-                        + (quickCrash ? "crashed quickly" : "over process crash limit"));
-                EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
-                        app.userId, app.processName, app.uid);
-                mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
-                if (!app.isPersistent()) {
-                    // We don't want to start this process again until the user
-                    // explicitly does so...  but for persistent process, we really
-                    // need to keep it running.  If a persistent process is actually
-                    // repeatedly crashing, then badness for everyone.
-                    EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
-                            app.processName);
-                    if (!app.isolated) {
-                        // XXX We don't have a way to mark isolated processes
-                        // as bad, since they don't have a persistent identity.
-                        markBadProcess(app.processName, app.uid,
-                                new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
-                        mProcessCrashTimes.remove(app.processName, app.uid);
-                        mProcessCrashCounts.remove(app.processName, app.uid);
-                    }
-                    app.bad = true;
-                    app.removed = true;
-                    // Don't let services in this process be restarted and potentially
-                    // annoy the user repeatedly.  Unless it is persistent, since those
-                    // processes run critical code.
-                    mService.mProcessList.removeProcessLocked(app, false, tryAgain,
-                            ApplicationExitInfo.REASON_CRASH, "crash");
-                    mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
-                    if (!showBackground) {
-                        return false;
-                    }
+        final boolean quickCrash = crashTime != null
+                && now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
+        if (quickCrash || isProcOverCrashLimitLBp(app, now)) {
+            // The process either crashed again very quickly or has been crashing periodically in
+            // the last few hours. If it was a bound foreground service, let's try to restart again
+            // in a while, otherwise the process loses!
+            Slog.w(TAG, "Process " + processName + " has crashed too many times, killing!"
+                    + " Reason: " + (quickCrash ? "crashed quickly" : "over process crash limit"));
+            EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
+                    userId, processName, uid);
+            mService.mAtmInternal.onHandleAppCrash(proc);
+            if (!persistent) {
+                // We don't want to start this process again until the user
+                // explicitly does so...  but for persistent process, we really
+                // need to keep it running.  If a persistent process is actually
+                // repeatedly crashing, then badness for everyone.
+                EventLog.writeEvent(EventLogTags.AM_PROC_BAD, userId, uid,
+                        processName);
+                if (!isolated) {
+                    // XXX We don't have a way to mark isolated processes
+                    // as bad, since they don't have a persistent identity.
+                    markBadProcess(processName, app.uid,
+                            new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
+                    mProcessCrashTimes.remove(processName, app.uid);
+                    mProcessCrashCounts.remove(processName, app.uid);
                 }
+                errState.setBad(true);
+                app.setRemoved(true);
+                // Don't let services in this process be restarted and potentially
+                // annoy the user repeatedly.  Unless it is persistent, since those
+                // processes run critical code.
+                mService.mProcessList.removeProcessLocked(app, false, tryAgain,
+                        ApplicationExitInfo.REASON_CRASH, "crash");
                 mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
-            } else {
-                final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
-                        app.getWindowProcessController(), reason);
-                if (data != null) {
-                    data.taskId = affectedTaskId;
-                }
-                if (data != null && crashTimePersistent != null
-                        && now < crashTimePersistent
-                        + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
-                    data.repeating = true;
+                if (!showBackground) {
+                    return false;
                 }
             }
-
-            if (data != null && tryAgain) {
-                data.isRestartableForService = true;
+            mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
+        } else {
+            final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
+                            proc, reason);
+            if (data != null) {
+                data.taskId = affectedTaskId;
             }
-
-            // If the crashing process is what we consider to be the "home process" and it has been
-            // replaced by a third-party app, clear the package preferred activities from packages
-            // with a home activity running in the process to prevent a repeatedly crashing app
-            // from blocking the user to manually clear the list.
-            final WindowProcessController proc = app.getWindowProcessController();
-            if (proc.isHomeProcess() && proc.hasActivities()
-                    && (app.info.flags & FLAG_SYSTEM) == 0) {
-                proc.clearPackagePreferredForHomeActivities();
-            }
-
-            if (!app.isolated) {
-                // XXX Can't keep track of crash times for isolated processes,
-                // because they don't have a persistent identity.
-                mProcessCrashTimes.put(app.processName, app.uid, now);
-                mProcessCrashTimesPersistent.put(app.processName, app.uid, now);
-                updateProcessCrashCountLBp(app.processName, app.uid, now);
+            if (data != null && crashTimePersistent != null
+                    && now < crashTimePersistent + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
+                data.repeating = true;
             }
         }
 
-        if (app.crashHandler != null) mService.mHandler.post(app.crashHandler);
+        if (data != null && tryAgain) {
+            data.isRestartableForService = true;
+        }
+
+        // If the crashing process is what we consider to be the "home process" and it has been
+        // replaced by a third-party app, clear the package preferred activities from packages
+        // with a home activity running in the process to prevent a repeatedly crashing app
+        // from blocking the user to manually clear the list.
+        if (proc.isHomeProcess() && proc.hasActivities() && (app.info.flags & FLAG_SYSTEM) == 0) {
+            proc.clearPackagePreferredForHomeActivities();
+        }
+
+        if (!isolated) {
+            // XXX Can't keep track of crash times for isolated processes,
+            // because they don't have a persistent identity.
+            mProcessCrashTimes.put(processName, uid, now);
+            mProcessCrashTimesPersistent.put(processName, uid, now);
+            updateProcessCrashCountLBp(processName, uid, now);
+        }
+
+        if (errState.getCrashHandler() != null) {
+            mService.mHandler.post(errState.getCrashHandler());
+        }
         return true;
     }
 
@@ -951,15 +959,16 @@
                 mService.mUserController.getCurrentUserId()) != 0;
 
         final int userId;
-        synchronized (mService) {
+        synchronized (mProcLock) {
             final ProcessRecord proc = data.proc;
             final AppErrorResult res = data.result;
             if (proc == null) {
                 Slog.e(TAG, "handleShowAppErrorUi: proc is null");
                 return;
             }
+            final ProcessErrorStateRecord errState = proc.mErrorState;
             userId = proc.userId;
-            if (proc.getDialogController().hasCrashDialogs()) {
+            if (errState.getDialogController().hasCrashDialogs()) {
                 Slog.e(TAG, "App already has crash dialog: " + proc);
                 if (res != null) {
                     res.set(AppErrorDialog.ALREADY_SHOWING);
@@ -968,7 +977,7 @@
             }
             boolean isBackground = (UserHandle.getAppId(proc.uid)
                     >= Process.FIRST_APPLICATION_UID
-                    && proc.pid != MY_PID);
+                    && proc.getPid() != MY_PID);
             for (int profileId : mService.mUserController.getCurrentProfileIds()) {
                 isBackground &= (userId != profileId);
             }
@@ -1001,7 +1010,7 @@
                 if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
                         && !crashSilenced && !shouldThottle
                         && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
-                    proc.getDialogController().showCrashDialogs(data);
+                    errState.getDialogController().showCrashDialogs(data);
                     if (!proc.isolated) {
                         mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now);
                     }
@@ -1026,17 +1035,19 @@
 
     void handleShowAnrUi(Message msg) {
         List<VersionedPackage> packageList = null;
-        synchronized (mService) {
-            AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
-            final ProcessRecord proc = data.proc;
-            if (proc == null) {
-                Slog.e(TAG, "handleShowAnrUi: proc is null");
-                return;
-            }
+        boolean doKill = false;
+        AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
+        final ProcessRecord proc = data.proc;
+        if (proc == null) {
+            Slog.e(TAG, "handleShowAnrUi: proc is null");
+            return;
+        }
+        synchronized (mProcLock) {
+            final ProcessErrorStateRecord errState = proc.mErrorState;
             if (!proc.isPersistent()) {
                 packageList = proc.getPackageListWithVersionCode();
             }
-            if (proc.getDialogController().hasAnrDialogs()) {
+            if (errState.getDialogController().hasAnrDialogs()) {
                 Slog.e(TAG, "App already has anr dialog: " + proc);
                 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
                         AppNotRespondingDialog.ALREADY_SHOWING);
@@ -1047,14 +1058,17 @@
                     Settings.Secure.ANR_SHOW_BACKGROUND, 0,
                     mService.mUserController.getCurrentUserId()) != 0;
             if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) {
-                proc.getDialogController().showAnrDialogs(data);
+                errState.getDialogController().showAnrDialogs(data);
             } else {
                 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
                         AppNotRespondingDialog.CANT_SHOW);
                 // Just kill the app if there is no dialog to be shown.
-                mService.killAppAtUsersRequest(proc);
+                doKill = true;
             }
         }
+        if (doKill) {
+            mService.killAppAtUsersRequest(proc);
+        }
         // Notify PackageWatchdog without the lock held
         if (packageList != null) {
             mPackageWatchdog.onPackageFailure(packageList,
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 48aa8be..17be210 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -920,20 +920,20 @@
             info = new ApplicationExitInfo();
         }
 
-        synchronized (mService) {
-            final int definingUid = app.hostingRecord != null
-                    ? app.hostingRecord.getDefiningUid() : 0;
-            info.setPid(app.pid);
+        synchronized (mService.mProcLock) {
+            final int definingUid = app.getHostingRecord() != null
+                    ? app.getHostingRecord().getDefiningUid() : 0;
+            info.setPid(app.getPid());
             info.setRealUid(app.uid);
             info.setPackageUid(app.info.uid);
             info.setDefiningUid(definingUid > 0 ? definingUid : app.info.uid);
             info.setProcessName(app.processName);
-            info.setConnectionGroup(app.connectionGroup);
+            info.setConnectionGroup(app.mServices.getConnectionGroup());
             info.setPackageName(app.info.packageName);
             info.setPackageList(app.getPackageList());
             info.setReason(ApplicationExitInfo.REASON_UNKNOWN);
             info.setStatus(0);
-            info.setImportance(procStateToImportance(app.setProcState));
+            info.setImportance(procStateToImportance(app.mState.getSetProcState()));
             info.setPss(app.mProfile.getLastPss());
             info.setRss(app.mProfile.getLastRss());
             info.setTimestamp(System.currentTimeMillis());
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 96e6f6e..77d2898 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -63,7 +63,7 @@
                 ? data.aInfo.loadLabel(context.getPackageManager())
                 : null;
         CharSequence name2 = null;
-        if ((mProc.getPkgList().size() == 1)
+        if (mProc.getPkgList().size() == 1
                 && (name2 = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
             if (name1 != null) {
                 resid = com.android.internal.R.string.anr_activity_application;
@@ -108,7 +108,7 @@
 
         final TextView report = findViewById(com.android.internal.R.id.aerr_report);
         report.setOnClickListener(this);
-        final boolean hasReceiver = mProc.errorReportReceiver != null;
+        final boolean hasReceiver = mProc.mErrorState.getErrorReportReceiver() != null;
         report.setVisibility(hasReceiver ? View.VISIBLE : View.GONE);
         final TextView close = findViewById(com.android.internal.R.id.aerr_close);
         close.setOnClickListener(this);
@@ -152,15 +152,18 @@
                     // Continue waiting for the application.
                     synchronized (mService) {
                         ProcessRecord app = mProc;
+                        final ProcessErrorStateRecord errState = app.mErrorState;
 
                         if (msg.what == WAIT_AND_REPORT) {
-                            appErrorIntent = mService.mAppErrors.createAppErrorIntentLocked(app,
+                            appErrorIntent = mService.mAppErrors.createAppErrorIntentLOSP(app,
                                     System.currentTimeMillis(), null);
                         }
 
-                        app.setNotResponding(false);
-                        app.notRespondingReport = null;
-                        app.getDialogController().clearAnrDialogs();
+                        synchronized (mService.mProcLock) {
+                            errState.setNotResponding(false);
+                            errState.setNotRespondingReport(null);
+                            errState.getDialogController().clearAnrDialogs();
+                        }
                         mService.mServices.scheduleServiceTimeoutLocked(app);
                     }
                     break;
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 3df058c..8f11a5a 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -207,7 +207,6 @@
      */
     private volatile boolean mTestPssMode = false;
 
-    @GuardedBy("mService")
     private final LowMemDetector mLowMemDetector;
 
     /**
@@ -239,13 +238,13 @@
     /**
      * Total time spent with RAM that has been added in the past since the last idle time.
      */
-    @GuardedBy("mService")
+    @GuardedBy("mProcLock")
     private long mLowRamTimeSinceLastIdle = 0;
 
     /**
      * If RAM is currently low, when that horrible situation started.
      */
-    @GuardedBy("mService")
+    @GuardedBy("mProcLock")
     private long mLowRamStartTime = 0;
 
     /**
@@ -331,6 +330,8 @@
      */
     final Object mProfilerLock = new Object();
 
+    final ActivityManagerGlobalLock mProcLock;
+
     /**
      * Observe DeviceConfig changes to the PSS calculation interval
      */
@@ -852,8 +853,8 @@
     /**
      * Schedule PSS collection of all processes.
      */
-    @GuardedBy("mService")
-    void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) {
+    @GuardedBy("mProcLock")
+    void requestPssAllProcsLPr(long now, boolean always, boolean memLowered) {
         synchronized (mProfilerLock) {
             if (!always) {
                 if (now < (mLastFullPssTime
@@ -870,10 +871,9 @@
             for (int i = mPendingPssProfiles.size() - 1; i >= 0; i--) {
                 mPendingPssProfiles.get(i).abortNextPssTime();
             }
-            mPendingPssProfiles.ensureCapacity(mService.mProcessList.getLruSizeLocked());
+            mPendingPssProfiles.ensureCapacity(mService.mProcessList.getLruSizeLOSP());
             mPendingPssProfiles.clear();
-            for (int i = mService.mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
-                ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+            mService.mProcessList.forEachLruProcessesLOSP(false, app -> {
                 final ProcessProfileRecord profile = app.mProfile;
                 if (profile.getThread() == null
                         || profile.getSetProcState() == PROCESS_STATE_NONEXISTENT) {
@@ -889,7 +889,7 @@
                     updateNextPssTimeLPf(profile.getSetProcState(), profile, now, true);
                     mPendingPssProfiles.add(profile);
                 }
-            }
+            });
             if (!mBgHandler.hasMessages(BgHandler.COLLECT_PSS_BG_MSG)) {
                 mBgHandler.sendEmptyMessage(BgHandler.COLLECT_PSS_BG_MSG);
             }
@@ -897,12 +897,12 @@
     }
 
     void setTestPssMode(boolean enabled) {
-        synchronized (mService) {
+        synchronized (mProcLock) {
             mTestPssMode = enabled;
             if (enabled) {
                 // Whenever we enable the mode, we want to take a snapshot all of current
                 // process mem use.
-                requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, true);
+                requestPssAllProcsLPr(SystemClock.uptimeMillis(), true, true);
             }
         }
     }
@@ -921,8 +921,8 @@
         return mLastMemoryLevel <= ADJ_MEM_FACTOR_NORMAL;
     }
 
-    @GuardedBy("mService")
-    void updateLowRamTimestampLocked(long now) {
+    @GuardedBy("mProcLock")
+    void updateLowRamTimestampLPr(long now) {
         mLowRamTimeSinceLastIdle = 0;
         if (mLowRamStartTime != 0) {
             mLowRamStartTime = now;
@@ -939,9 +939,8 @@
         mMemFactorOverride = factor;
     }
 
-    @GuardedBy("mService")
-    boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) {
-        final int numOfLru = mService.mProcessList.getLruSizeLocked();
+    @GuardedBy({"mService", "mProcLock"})
+    boolean updateLowMemStateLSP(int numCached, int numEmpty, int numTrimming) {
         final long now = SystemClock.uptimeMillis();
         int memFactor;
         if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {
@@ -973,7 +972,7 @@
         if (DEBUG_OOM_ADJ) {
             Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor + " override=" + mMemFactorOverride
                     + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
-                    + " numProcs=" + mService.mProcessList.getLruSizeLocked()
+                    + " numProcs=" + mService.mProcessList.getLruSizeLOSP()
                     + " last=" + mLastNumProcesses);
         }
         boolean override;
@@ -982,7 +981,7 @@
         }
         if (memFactor > mLastMemoryLevel) {
             if (!override && (!mAllowLowerMemLevel
-                    || mService.mProcessList.getLruSizeLocked() >= mLastNumProcesses)) {
+                    || mService.mProcessList.getLruSizeLOSP() >= mLastNumProcesses)) {
                 memFactor = mLastMemoryLevel;
                 if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
             }
@@ -992,7 +991,7 @@
             FrameworkStatsLog.write(FrameworkStatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
         }
         mLastMemoryLevel = memFactor;
-        mLastNumProcesses = mService.mProcessList.getLruSizeLocked();
+        mLastNumProcesses = mService.mProcessList.getLruSizeLOSP();
         boolean allChanged;
         int trackerMemFactor;
         synchronized (mService.mProcessStats.mLock) {
@@ -1004,7 +1003,6 @@
             if (mLowRamStartTime == 0) {
                 mLowRamStartTime = now;
             }
-            int step = 0;
             int fgTrimLevel;
             switch (memFactor) {
                 case ADJ_MEM_FACTOR_CRITICAL:
@@ -1022,126 +1020,129 @@
             if (mHasHomeProcess) minFactor++;
             if (mHasPreviousProcess) minFactor++;
             if (factor < minFactor) factor = minFactor;
-            int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
-            for (int i = 0; i < numOfLru; i++) {
-                final ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+            final int actualFactor = factor;
+            final int[] step = {0};
+            final int[] curLevel = {ComponentCallbacks2.TRIM_MEMORY_COMPLETE};
+            mService.mProcessList.forEachLruProcessesLOSP(true, app -> {
                 final ProcessProfileRecord profile = app.mProfile;
                 final int trimMemoryLevel = profile.getTrimMemoryLevel();
-                if (allChanged || app.procStateChanged) {
-                    mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
-                    app.procStateChanged = false;
+                final ProcessStateRecord state = app.mState;
+                final int curProcState = state.getCurProcState();
+                IApplicationThread thread;
+                if (allChanged || state.hasProcStateChanged()) {
+                    mService.setProcessTrackerStateLOSP(app, trackerMemFactor, now);
+                    state.setProcStateChanged(false);
                 }
-                if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
-                        && !app.killedByAm) {
-                    if (trimMemoryLevel < curLevel && app.thread != null) {
+                if (curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.isKilledByAm()) {
+                    if (trimMemoryLevel < curLevel[0] && (thread = app.getThread()) != null) {
                         try {
                             if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
                                 Slog.v(TAG_OOM_ADJ,
                                         "Trimming memory of " + app.processName
-                                        + " to " + curLevel);
+                                        + " to " + curLevel[0]);
                             }
-                            app.thread.scheduleTrimMemory(curLevel);
+                            thread.scheduleTrimMemory(curLevel[0]);
                         } catch (RemoteException e) {
                         }
                     }
-                    profile.setTrimMemoryLevel(curLevel);
-                    step++;
-                    if (step >= factor) {
-                        step = 0;
-                        switch (curLevel) {
+                    profile.setTrimMemoryLevel(curLevel[0]);
+                    step[0]++;
+                    if (step[0] >= actualFactor) {
+                        step[0] = 0;
+                        switch (curLevel[0]) {
                             case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
-                                curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
+                                curLevel[0] = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
                                 break;
                             case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
-                                curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+                                curLevel[0] = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
                                 break;
                         }
                     }
-                } else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
-                        && !app.killedByAm) {
+                } else if (curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+                        && !app.isKilledByAm()) {
                     if (trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
-                            && app.thread != null) {
+                            && (thread = app.getThread()) != null) {
                         try {
                             if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
                                 Slog.v(TAG_OOM_ADJ,
                                         "Trimming memory of heavy-weight " + app.processName
                                         + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
                             }
-                            app.thread.scheduleTrimMemory(
+                            thread.scheduleTrimMemory(
                                     ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
                         } catch (RemoteException e) {
                         }
                     }
                     profile.setTrimMemoryLevel(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
                 } else {
-                    if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-                            || app.systemNoUi) && profile.hasPendingUiClean()) {
+                    if ((curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                                || state.isSystemNoUi()) && profile.hasPendingUiClean()) {
                         // If this application is now in the background and it
                         // had done UI, then give it the special trim level to
                         // have it free UI resources.
                         final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
-                        if (trimMemoryLevel < level && app.thread != null) {
+                        if (trimMemoryLevel < level && (thread = app.getThread()) != null) {
                             try {
                                 if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
                                     Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui "
                                             + app.processName + " to " + level);
                                 }
-                                app.thread.scheduleTrimMemory(level);
+                                thread.scheduleTrimMemory(level);
                             } catch (RemoteException e) {
                             }
                         }
                         profile.setPendingUiClean(false);
                     }
-                    if (trimMemoryLevel < fgTrimLevel && app.thread != null) {
+                    if (trimMemoryLevel < fgTrimLevel && (thread = app.getThread()) != null) {
                         try {
                             if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
                                 Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName
                                         + " to " + fgTrimLevel);
                             }
-                            app.thread.scheduleTrimMemory(fgTrimLevel);
+                            thread.scheduleTrimMemory(fgTrimLevel);
                         } catch (RemoteException e) {
                         }
                     }
                     profile.setTrimMemoryLevel(fgTrimLevel);
                 }
-            }
+            });
         } else {
             if (mLowRamStartTime != 0) {
                 mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
                 mLowRamStartTime = 0;
             }
-            for (int i = 0; i < numOfLru; i++) {
-                final ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+            mService.mProcessList.forEachLruProcessesLOSP(true, app -> {
                 final ProcessProfileRecord profile = app.mProfile;
-                if (allChanged || app.procStateChanged) {
-                    mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
-                    app.procStateChanged = false;
+                final IApplicationThread thread;
+                final ProcessStateRecord state = app.mState;
+                if (allChanged || state.hasProcStateChanged()) {
+                    mService.setProcessTrackerStateLOSP(app, trackerMemFactor, now);
+                    state.setProcStateChanged(false);
                 }
-                if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-                        || app.systemNoUi) && profile.hasPendingUiClean()) {
+                if ((state.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                            || state.isSystemNoUi()) && profile.hasPendingUiClean()) {
                     if (profile.getTrimMemoryLevel() < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
-                            && app.thread != null) {
+                            && (thread = app.getThread()) != null) {
                         try {
                             if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
                                 Slog.v(TAG_OOM_ADJ,
                                         "Trimming memory of ui hidden " + app.processName
                                         + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
                             }
-                            app.thread.scheduleTrimMemory(
-                                    ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+                            thread.scheduleTrimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
                         } catch (RemoteException e) {
                         }
                     }
                     profile.setPendingUiClean(false);
                 }
                 profile.setTrimMemoryLevel(0);
-            }
+            });
         }
         return allChanged;
     }
 
-    @GuardedBy("mService")
-    long getLowRamTimeSinceIdleLocked(long now) {
+    @GuardedBy("mProcLock")
+    long getLowRamTimeSinceIdleLPr(long now) {
         return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now - mLowRamStartTime) : 0);
     }
 
@@ -1262,7 +1263,7 @@
         // If there are no longer any background processes running,
         // and the app that died was not running instrumentation,
         // then tell everyone we are now low on memory.
-        if (!mService.mProcessList.haveBackgroundProcessLocked()) {
+        if (!mService.mProcessList.haveBackgroundProcessLOSP()) {
             boolean doReport = Build.IS_DEBUGGABLE;
             final long now = SystemClock.uptimeMillis();
             if (doReport) {
@@ -1272,19 +1273,19 @@
                     mLastMemUsageReportTime = now;
                 }
             }
-            final int lruSize = mService.mProcessList.getLruSizeLocked();
+            final int lruSize = mService.mProcessList.getLruSizeLOSP();
             final ArrayList<ProcessMemInfo> memInfos = doReport
                     ? new ArrayList<ProcessMemInfo>(lruSize) : null;
             EventLogTags.writeAmLowMemory(lruSize);
-            for (int i = lruSize - 1; i >= 0; i--) {
-                ProcessRecord rec = mService.mProcessList.mLruProcesses.get(i);
-                if (rec == dyingProc || rec.thread == null) {
+            mService.mProcessList.forEachLruProcessesLOSP(false, rec -> {
+                if (rec == dyingProc || rec.getThread() == null) {
                     return;
                 }
+                final ProcessStateRecord state = rec.mState;
                 if (memInfos != null) {
-                    memInfos.add(new ProcessMemInfo(rec.processName, rec.pid,
-                                rec.setAdj, rec.setProcState,
-                                rec.adjType, rec.makeAdjReason()));
+                    memInfos.add(new ProcessMemInfo(rec.processName, rec.getPid(),
+                                state.getSetAdj(), state.getSetProcState(),
+                                state.getAdjType(), state.makeAdjReason()));
                 }
                 final ProcessProfileRecord profile = rec.mProfile;
                 if ((profile.getLastLowMemory() + mService.mConstants.GC_MIN_INTERVAL) <= now) {
@@ -1292,7 +1293,7 @@
                     // state for a GC request.  Make sure to do
                     // heavy/important/visible/foreground processes first.
                     synchronized (mProfilerLock) {
-                        if (rec.setAdj <= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
+                        if (state.getSetAdj() <= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
                             profile.setLastRequestedGc(0);
                         } else {
                             profile.setLastRequestedGc(profile.getLastLowMemory());
@@ -1303,7 +1304,7 @@
                         addProcessToGcListLPf(rec);
                     }
                 }
-            }
+            });
             if (doReport) {
                 Message msg = mService.mHandler.obtainMessage(REPORT_MEM_USAGE_MSG, memInfos);
                 mService.mHandler.sendMessage(msg);
@@ -1554,7 +1555,9 @@
             PrintWriter catPw = new FastPrintWriter(catSw, false, 256);
             String[] emptyArgs = new String[] { };
             catPw.println();
-            mService.mProcessList.dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null, -1);
+            synchronized (mProcLock) {
+                mService.mProcessList.dumpProcessesLSP(null, catPw, emptyArgs, 0, false, null, -1);
+            }
             catPw.println();
             mService.mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0,
                     false, null).dumpLocked();
@@ -1575,7 +1578,7 @@
         }
     }
 
-    @GuardedBy("mService")
+    @GuardedBy("mProfilerLock")
     private void stopProfilerLPf(ProcessRecord proc, int profileType) {
         if (proc == null || proc == mProfileData.getProfileProc()) {
             proc = mProfileData.getProfileProc();
@@ -1644,7 +1647,7 @@
                 }
                 mProfileData.getProfilerInfo().profileFd = null;
 
-                if (proc.pid == mService.MY_PID) {
+                if (proc.getPid() == mService.MY_PID) {
                     // When profiling the system server itself, avoid closing the file
                     // descriptor, as profilerControl will not create a copy.
                     // Note: it is also not correct to just set profileFd to null, as the
@@ -1930,6 +1933,7 @@
 
     AppProfiler(ActivityManagerService service, Looper bgLooper, LowMemDetector detector) {
         mService = service;
+        mProcLock = service.mProcLock;
         mBgHandler = new BgHandler(bgLooper);
         mLowMemDetector = detector;
         mProcessCpuThread = new ProcessCpuThread("CpuTracker");
@@ -2027,19 +2031,21 @@
                     i >= 0 && app.getActiveInstrumentation() == null; i--) {
                 ActiveInstrumentation aInstr = mService.mActiveInstrumentation.get(i);
                 if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) {
-                    if (aInstr.mTargetProcesses.length == 0) {
-                        // This is the wildcard mode, where every process brought up for
-                        // the target instrumentation should be included.
-                        if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
-                            app.setActiveInstrumentation(aInstr);
-                            aInstr.mRunningProcesses.add(app);
-                        }
-                    } else {
-                        for (String proc : aInstr.mTargetProcesses) {
-                            if (proc.equals(app.processName)) {
+                    synchronized (mProcLock) {
+                        if (aInstr.mTargetProcesses.length == 0) {
+                            // This is the wildcard mode, where every process brought up for
+                            // the target instrumentation should be included.
+                            if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
                                 app.setActiveInstrumentation(aInstr);
                                 aInstr.mRunningProcesses.add(app);
-                                break;
+                            }
+                        } else {
+                            for (String proc : aInstr.mTargetProcesses) {
+                                if (proc.equals(app.processName)) {
+                                    app.setActiveInstrumentation(aInstr);
+                                    aInstr.mRunningProcesses.add(app);
+                                    break;
+                                }
                             }
                         }
                     }
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 170c34d..c6947c2d 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -207,12 +207,13 @@
             if (mPowerStatsInternal != null) {
                 populateEnergyConsumerSubsystemMapsLocked();
                 final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData();
-                final boolean[] supportedBuckets = getSupportedEnergyBuckets(
-                        initialMeasuredEnergies);
                 mMeasuredEnergySnapshot = initialMeasuredEnergies == null
                         ? null : new MeasuredEnergySnapshot(initialMeasuredEnergies);
+                final boolean[] supportedStdBuckets
+                        = getSupportedEnergyBuckets(initialMeasuredEnergies);
+                final int numCustomBuckets = 0; // TODO: Get this from initialMeasuredEnergies
                 synchronized (mStats) {
-                    mStats.initMeasuredEnergyStatsLocked(supportedBuckets);
+                    mStats.initMeasuredEnergyStatsLocked(supportedStdBuckets, numCustomBuckets);
                 }
             }
         }
@@ -740,15 +741,16 @@
 
     /**
      * Map the {@link MeasuredEnergyArray.MeasuredEnergySubsystem}s in the given energyArray to
-     * their corresponding {@link MeasuredEnergyStats.EnergyBucket}s.
+     * their corresponding {@link MeasuredEnergyStats.StandardEnergyBucket}s.
+     * Does not include custom energy buckets (which are always, by definition, supported).
      *
-     * @return array with true for index i if energy bucket i is supported.
+     * @return array with true for index i if standard energy bucket i is supported.
      */
     private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) {
         if (energyArray == null) {
             return null;
         }
-        final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
+        final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS];
         final int size = energyArray.size();
         for (int energyIdx = 0; energyIdx < size; energyIdx++) {
             switch (energyArray.getSubsystem(energyIdx)) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c287240..773e313 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -21,6 +21,10 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.State;
+import android.hardware.power.stats.StateResidency;
+import android.hardware.power.stats.StateResidencyResult;
 import android.net.INetworkManagementEventObserver;
 import android.net.NetworkCapabilities;
 import android.os.BatteryStats;
@@ -51,6 +55,7 @@
 import android.os.health.HealthStatsParceler;
 import android.os.health.HealthStatsWriter;
 import android.os.health.UidHealthStats;
+import android.power.PowerStatsInternal;
 import android.provider.Settings;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.ModemActivityInfo;
@@ -87,10 +92,13 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 
 /**
  * All information we are collecting about things that can happen that impact
@@ -112,23 +120,23 @@
     private final BatteryExternalStatsWorker mWorker;
     private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
 
-    private native void getLowPowerStats(RpmStats rpmStats);
-    private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
-    private native int getSubsystemLowPowerStats(ByteBuffer outBuffer);
     private native void getRailEnergyPowerStats(RailStats railStats);
     private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8
                     .newDecoder()
                     .onMalformedInput(CodingErrorAction.REPLACE)
                     .onUnmappableCharacter(CodingErrorAction.REPLACE)
                     .replaceWith("?");
-    private ByteBuffer mUtf8BufferStat = ByteBuffer.allocateDirect(MAX_LOW_POWER_STATS_SIZE);
-    private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE);
     private static final int MAX_LOW_POWER_STATS_SIZE = 4096;
+    private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
 
     private final HandlerThread mHandlerThread;
     private final Handler mHandler;
     private final Object mLock = new Object();
 
+    private PowerStatsInternal mPowerStatsInternal = null;
+    private Map<Integer, String> mEntityNames = new HashMap();
+    private Map<Integer, Map<Integer, String>> mStateNames = new HashMap();
+
     @GuardedBy("mStats")
     private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
     @GuardedBy("mStats")
@@ -163,16 +171,57 @@
                 }
             };
 
+    private void populatePowerEntityMaps() {
+        if (mPowerStatsInternal == null) {
+            // PowerStatsInternal unavailable, don't bother populating maps.
+            mEntityNames = null;
+            mStateNames = null;
+            return;
+        }
+
+        PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
+        if (entities == null) {
+            return;
+        }
+
+        for (int i = 0; i < entities.length; i++) {
+            final PowerEntity entity = entities[i];
+            Map<Integer, String> states = new HashMap();
+            for (int j = 0; j < entity.states.length; j++) {
+                final State state = entity.states[j];
+                states.put(state.id, state.name);
+            }
+
+            mEntityNames.put(entity.id, entity.name);
+            mStateNames.put(entity.id, states);
+        }
+    }
+
     /**
      * Replaces the information in the given rpmStats with up-to-date information.
      */
     @Override
     public void fillLowPowerStats(RpmStats rpmStats) {
-        if (DBG) Slog.d(TAG, "begin getLowPowerStats");
+        final StateResidencyResult[] results;
         try {
-            getLowPowerStats(rpmStats);
-        } finally {
-            if (DBG) Slog.d(TAG, "end getLowPowerStats");
+            results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
+                    .get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to getStateResidencyAsync", e);
+            return;
+        }
+
+        for (int i = 0; i < results.length; i++) {
+            final StateResidencyResult result = results[i];
+            RpmStats.PowerStateSubsystem subsystem =
+                    rpmStats.getSubsystem(mEntityNames.get(result.id));
+
+            for (int j = 0; j < result.stateResidencyData.length; j++) {
+                final StateResidency stateResidency = result.stateResidencyData[j];
+                subsystem.putState(mStateNames.get(result.id).get(stateResidency.id),
+                        stateResidency.totalTimeInStateMs,
+                        (int) stateResidency.totalStateEntryCount);
+            }
         }
     }
 
@@ -187,47 +236,46 @@
     }
 
     @Override
-    public String getPlatformLowPowerStats() {
-        if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats");
-        try {
-            mUtf8BufferStat.clear();
-            mUtf16BufferStat.clear();
-            mDecoderStat.reset();
-            int bytesWritten = getPlatformLowPowerStats(mUtf8BufferStat);
-            if (bytesWritten < 0) {
-                return null;
-            } else if (bytesWritten == 0) {
-                return "Empty";
-            }
-            mUtf8BufferStat.limit(bytesWritten);
-            mDecoderStat.decode(mUtf8BufferStat, mUtf16BufferStat, true);
-            mUtf16BufferStat.flip();
-            return mUtf16BufferStat.toString();
-        } finally {
-            if (DBG) Slog.d(TAG, "end getPlatformLowPowerStats");
-        }
-    }
-
-    @Override
     public String getSubsystemLowPowerStats() {
-        if (DBG) Slog.d(TAG, "begin getSubsystemLowPowerStats");
+        final StateResidencyResult[] results;
         try {
-            mUtf8BufferStat.clear();
-            mUtf16BufferStat.clear();
-            mDecoderStat.reset();
-            int bytesWritten = getSubsystemLowPowerStats(mUtf8BufferStat);
-            if (bytesWritten < 0) {
-                return null;
-            } else if (bytesWritten == 0) {
-                return "Empty";
-            }
-            mUtf8BufferStat.limit(bytesWritten);
-            mDecoderStat.decode(mUtf8BufferStat, mUtf16BufferStat, true);
-            mUtf16BufferStat.flip();
-            return mUtf16BufferStat.toString();
-        } finally {
-            if (DBG) Slog.d(TAG, "end getSubsystemLowPowerStats");
+            results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
+                    .get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to getStateResidencyAsync", e);
+            return "Empty";
         }
+
+        if (results.length == 0) return "Empty";
+
+        int charsLeft = MAX_LOW_POWER_STATS_SIZE;
+        StringBuilder builder = new StringBuilder("SubsystemPowerState");
+        for (int i = 0; i < results.length; i++) {
+            final StateResidencyResult result = results[i];
+            StringBuilder subsystemBuilder = new StringBuilder();
+            subsystemBuilder.append(" subsystem_" + i);
+            subsystemBuilder.append(" name=" + mEntityNames.get(result.id));
+
+            for (int j = 0; j < result.stateResidencyData.length; j++) {
+                final StateResidency stateResidency = result.stateResidencyData[j];
+                subsystemBuilder.append(" state_" + j);
+                subsystemBuilder.append(" name=" + mStateNames.get(result.id).get(
+                        stateResidency.id));
+                subsystemBuilder.append(" time=" + stateResidency.totalTimeInStateMs);
+                subsystemBuilder.append(" count=" + stateResidency.totalStateEntryCount);
+                subsystemBuilder.append(" last entry=" + stateResidency.lastEntryTimestampMs);
+            }
+
+            if (subsystemBuilder.length() <= charsLeft) {
+                charsLeft -= subsystemBuilder.length();
+                builder.append(subsystemBuilder);
+            } else {
+                Slog.e(TAG, "getSubsystemLowPowerStats: buffer not enough");
+                break;
+            }
+        }
+
+        return builder.toString();
     }
 
     BatteryStatsService(Context context, File systemDir, Handler handler) {
@@ -274,6 +322,11 @@
         } catch (RemoteException e) {
             Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
         }
+        mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
+        if (mPowerStatsInternal != null) {
+            populatePowerEntityMaps();
+        }
+
         Watchdog.getInstance().addMonitor(this);
     }
 
@@ -561,10 +614,10 @@
      * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
      * and per-UID basis.
      */
-    public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
+    public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
         mContext.enforceCallingPermission(
                 android.Manifest.permission.BATTERY_STATS, null);
-        return mBatteryUsageStatsProvider.getBatteryUsageStats(query);
+        return mBatteryUsageStatsProvider.getBatteryUsageStats(queries);
     }
 
     public byte[] getStatistics() {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 4de1218..7e65434 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -26,6 +26,7 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
+import android.app.IApplicationThread;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -42,6 +43,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerWhitelistManager.TempAllowListType;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -221,7 +223,7 @@
     }
 
     public boolean isPendingBroadcastProcessLocked(int pid) {
-        return mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid;
+        return mPendingBroadcast != null && mPendingBroadcast.curApp.getPid() == pid;
     }
 
     public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
@@ -285,19 +287,21 @@
             ProcessRecord app) throws RemoteException {
         if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                 "Process cur broadcast " + r + " for app " + app);
-        if (app.thread == null) {
+        final IApplicationThread thread = app.getThread();
+        if (thread == null) {
             throw new RemoteException();
         }
-        if (app.inFullBackup) {
+        if (app.isInFullBackup()) {
             skipReceiverLocked(r);
             return;
         }
 
-        r.receiver = app.thread.asBinder();
+        r.receiver = thread.asBinder();
         r.curApp = app;
-        app.curReceivers.add(r);
-        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
-        mService.mProcessList.updateLruProcessLocked(app, false, null);
+        final ProcessReceiverRecord prr = app.mReceivers;
+        prr.addCurReceiver(r);
+        app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
+        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)).
@@ -314,10 +318,10 @@
                     + ": " + r);
             mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
                                       PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
-            app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
+            thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                     mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
                     r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
-                    app.getReportedProcState());
+                    app.mState.getReportedProcState());
             if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                     "Process cur broadcast " + r + " DELIVERED for app " + app);
             started = true;
@@ -327,7 +331,7 @@
                         "Process cur broadcast " + r + ": NOT STARTED!");
                 r.receiver = null;
                 r.curApp = null;
-                app.curReceivers.remove(r);
+                prr.removeCurReceiver(r);
             }
         }
     }
@@ -335,7 +339,7 @@
     public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
         boolean didSomething = false;
         final BroadcastRecord br = mPendingBroadcast;
-        if (br != null && br.curApp.pid > 0 && br.curApp.pid == app.pid) {
+        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);
@@ -362,7 +366,7 @@
 
     public void skipPendingBroadcastLocked(int pid) {
         final BroadcastRecord br = mPendingBroadcast;
-        if (br != null && br.curApp.pid == pid) {
+        if (br != null && br.curApp.getPid() == pid) {
             br.state = BroadcastRecord.IDLE;
             br.nextReceiver = mPendingBroadcastRecvIndex;
             mPendingBroadcast = null;
@@ -502,8 +506,8 @@
 
         r.receiver = null;
         r.intent.setComponent(null);
-        if (r.curApp != null && r.curApp.curReceivers.contains(r)) {
-            r.curApp.curReceivers.remove(r);
+        if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
+            r.curApp.mReceivers.removeCurReceiver(r);
             mService.enqueueOomAdjTargetLocked(r.curApp);
         }
         if (r.curFilter != null) {
@@ -577,12 +581,14 @@
             throws RemoteException {
         // Send the intent to the receiver asynchronously using one-way binder calls.
         if (app != null) {
-            if (app.thread != 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 {
-                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
-                            data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
+                    thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
+                            data, extras, ordered, sticky, sendingUser,
+                            app.mState.getReportedProcState());
                 // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
                 // DeadObjectException when the process isn't actually dead.
                 //} catch (DeadObjectException ex) {
@@ -592,8 +598,8 @@
                     // Failed to call into the process. It's either dying or wedged. Kill it gently.
                     synchronized (mService) {
                         Slog.w(TAG, "Can't deliver broadcast to " + app.processName
-                                + " (pid " + app.pid + "). Crashing it.");
-                        app.scheduleCrash("can't deliver broadcast");
+                                + " (pid " + app.getPid() + "). Crashing it.");
+                        app.scheduleCrashLocked("can't deliver broadcast");
                     }
                     throw ex;
                 }
@@ -723,8 +729,8 @@
             skip = true;
         }
 
-        if (!skip && (filter.receiverList.app == null || filter.receiverList.app.killed
-                || filter.receiverList.app.isCrashing())) {
+        if (!skip && (filter.receiverList.app == null || filter.receiverList.app.isKilled()
+                || filter.receiverList.app.mErrorState.isCrashing())) {
             Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
                     + " to " + filter.receiverList + ": process gone or crashing");
             skip = true;
@@ -793,7 +799,7 @@
                 // things that directly call the IActivityManager API, which
                 // are already core system stuff so don't matter for this.
                 r.curApp = filter.receiverList.app;
-                filter.receiverList.app.curReceivers.add(r);
+                filter.receiverList.app.mReceivers.addCurReceiver(r);
                 mService.enqueueOomAdjTargetLocked(r.curApp);
                 mService.updateOomAdjPendingTargetsLocked(
                         OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
@@ -805,7 +811,7 @@
         try {
             if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
                     "Delivering to " + filter + " : " + r);
-            if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
+            if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
                 // Skip delivery if full backup in progress
                 // If it's an ordered broadcast, we need to continue to the next receiver.
                 if (ordered) {
@@ -832,7 +838,7 @@
             if (filter.receiverList.app != null) {
                 filter.receiverList.app.removeAllowBackgroundActivityStartsToken(r);
                 if (ordered) {
-                    filter.receiverList.app.curReceivers.remove(r);
+                    filter.receiverList.app.mReceivers.removeCurReceiver(r);
                     // Something wrong, its oom adj could be downgraded, but not in a hurry.
                     mService.enqueueOomAdjTargetLocked(r.curApp);
                 }
@@ -855,8 +861,8 @@
         }
 
         final boolean callerForeground = receiverRecord.callerApp != null
-                ? receiverRecord.callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND
-                : true;
+                ? receiverRecord.callerApp.mState.getSetSchedGroup()
+                != ProcessList.SCHED_GROUP_BACKGROUND : true;
 
         // Show a permission review UI only for explicit broadcast from a foreground app
         if (callerForeground && receiverRecord.intent.getComponent() != null) {
@@ -897,7 +903,7 @@
     }
 
     final void scheduleTempWhitelistLocked(int uid, long duration, BroadcastRecord r,
-            @BroadcastOptions.TempAllowListType int type) {
+            @TempAllowListType int type) {
         if (duration > Integer.MAX_VALUE) {
             duration = Integer.MAX_VALUE;
         }
@@ -1017,16 +1023,16 @@
                     + mPendingBroadcast.curApp);
 
             boolean isDead;
-            if (mPendingBroadcast.curApp.pid > 0) {
+            if (mPendingBroadcast.curApp.getPid() > 0) {
                 synchronized (mService.mPidsSelfLocked) {
                     ProcessRecord proc = mService.mPidsSelfLocked.get(
-                            mPendingBroadcast.curApp.pid);
-                    isDead = proc == null || proc.isCrashing();
+                            mPendingBroadcast.curApp.getPid());
+                    isDead = proc == null || proc.mErrorState.isCrashing();
                 }
             } else {
-                final ProcessRecord proc = mService.mProcessList.mProcessNames.get(
+                final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(
                         mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
-                isDead = proc == null || !proc.pendingStart;
+                isDead = proc == null || !proc.isPendingStart();
             }
             if (!isDead) {
                 // It's still alive, so keep waiting
@@ -1496,7 +1502,7 @@
                     + " (uid " + r.callingUid + ")");
             skip = true;
         }
-        if (r.curApp != null && r.curApp.isCrashing()) {
+        if (r.curApp != null && r.curApp.mErrorState.isCrashing()) {
             // If the target process is crashing, just skip it.
             Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
                     + " to " + r.curApp + ": process crashing");
@@ -1547,14 +1553,14 @@
                 info.activityInfo.applicationInfo.uid, false);
 
         if (!skip) {
-            final int allowed = mService.getAppStartModeLocked(
+            final int allowed = mService.getAppStartModeLOSP(
                     info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
                     info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
             if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                 // We won't allow this receiver to be launched if the app has been
                 // completely disabled from launches, or it was not explicitly sent
                 // to it and the app is in a state that should not receive it
-                // (depending on how getAppStartModeLocked has determined that).
+                // (depending on how getAppStartModeLOSP has determined that).
                 if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
                     Slog.w(TAG, "Background execution disabled: receiving "
                             + r.intent + " to "
@@ -1629,7 +1635,7 @@
         }
 
         // Is this receiver's application already running?
-        if (app != null && app.thread != null && !app.killed) {
+        if (app != null && app.getThread() != null && !app.isKilled()) {
             try {
                 app.addPackage(info.activityInfo.packageName,
                         info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
@@ -1664,13 +1670,13 @@
         if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                 "Need to start app ["
                 + mQueueName + "] " + targetProcess + " for broadcast " + r);
-        if ((r.curApp=mService.startProcessLocked(targetProcess,
+        r.curApp = mService.startProcessLocked(targetProcess,
                 info.activityInfo.applicationInfo, true,
                 r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
-                new HostingRecord("broadcast", r.curComponent),
-                isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
-                (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
-                        == null) {
+                new HostingRecord("broadcast", r.curComponent), isActivityCapable
+                ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
+                (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false);
+        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 "
diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java
index 45ce4c5..50278fd 100644
--- a/services/core/java/com/android/server/am/CacheOomRanker.java
+++ b/services/core/java/com/android/server/am/CacheOomRanker.java
@@ -61,6 +61,7 @@
     private final Object mPhenotypeFlagLock = new Object();
 
     private final ActivityManagerService mService;
+    private final ActivityManagerGlobalLock mProcLock;
     private final Object mProfilerLock;
 
     @GuardedBy("mPhenotypeFlagLock")
@@ -106,6 +107,7 @@
 
     CacheOomRanker(final ActivityManagerService service) {
         mService = service;
+        mProcLock = service.mProcLock;
         mProfilerLock = service.mAppProfiler.mProfilerLock;
     }
 
@@ -179,15 +181,14 @@
      * Re-rank the cached processes in the lru list with a weighted ordering
      * of lru, rss size and number of times the process has been put in the cache.
      */
-    public void reRankLruCachedApps(ProcessList processList) {
+    @GuardedBy({"mService", "mProcLock"})
+    void reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart) {
         float lruWeight;
         float usesWeight;
         float rssWeight;
         int[] lruPositions;
         RankedProcessRecord[] scoredProcessRecords;
 
-        ArrayList<ProcessRecord> lruList = processList.mLruProcesses;
-
         synchronized (mPhenotypeFlagLock) {
             lruWeight = mLruWeight;
             usesWeight = mUsesWeight;
@@ -204,11 +205,11 @@
         // Collect the least recently used processes to re-rank, only rank cached
         // processes further down the list than mLruProcessServiceStart.
         int cachedProcessPos = 0;
-        for (int i = 0; i < processList.mLruProcessServiceStart
+        for (int i = 0; i < lruProcessServiceStart
                 && cachedProcessPos < scoredProcessRecords.length; ++i) {
             ProcessRecord app = lruList.get(i);
             // Processes that will be assigned a cached oom adj score.
-            if (!app.killedByAm && app.thread != null && app.curAdj
+            if (!app.isKilledByAm() && app.getThread() != null && app.mState.getCurAdj()
                     >= ProcessList.UNKNOWN_ADJ) {
                 scoredProcessRecords[cachedProcessPos].proc = app;
                 scoredProcessRecords[cachedProcessPos].score = 0.0f;
@@ -247,7 +248,8 @@
         if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
             boolean printedHeader = false;
             for (int i = 0; i < scoredProcessRecords.length; ++i) {
-                if (scoredProcessRecords[i].proc.pid != lruList.get(lruPositions[i]).pid) {
+                if (scoredProcessRecords[i].proc.getPid()
+                        != lruList.get(lruPositions[i]).getPid()) {
                     if (!printedHeader) {
                         Slog.i(OomAdjuster.TAG, "reRankLruCachedApps");
                         printedHeader = true;
@@ -291,15 +293,15 @@
     private static class LastActivityTimeComparator implements Comparator<RankedProcessRecord> {
         @Override
         public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
-            return Long.compare(o1.proc.lastActivityTime, o2.proc.lastActivityTime);
+            return Long.compare(o1.proc.getLastActivityTime(), o2.proc.getLastActivityTime());
         }
     }
 
     private static class CacheUseComparator implements Comparator<RankedProcessRecord> {
         @Override
         public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
-            return Long.compare(o1.proc.getCacheOomRankerUseCount(),
-                    o2.proc.getCacheOomRankerUseCount());
+            return Long.compare(o1.proc.mState.getCacheOomRankerUseCount(),
+                    o2.proc.mState.getCacheOomRankerUseCount());
         }
     }
 
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c558b3d..c18031f 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -42,7 +42,6 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.ServiceThread;
 
-import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -153,9 +152,14 @@
      */
     final ServiceThread mCachedAppOptimizerThread;
 
+    @GuardedBy("mProcLock")
     private final ArrayList<ProcessRecord> mPendingCompactionProcesses =
             new ArrayList<ProcessRecord>();
+
     private final ActivityManagerService mAm;
+
+    private final ActivityManagerGlobalLock mProcLock;
+
     private final OnPropertiesChangedListener mOnFlagsChangedListener =
             new OnPropertiesChangedListener() {
                 @Override
@@ -234,7 +238,7 @@
     @VisibleForTesting
     Handler mCompactionHandler;
     private Handler mFreezeHandler;
-    @GuardedBy("mAm")
+    @GuardedBy("mProcLock")
     private boolean mFreezerOverride = false;
 
     // Maps process ID to last compaction statistics for processes that we've fully compacted. Used
@@ -242,6 +246,7 @@
     // data for "some" compactions. Uses LinkedHashMap to ensure insertion order is kept and
     // facilitate removal of the oldest entry.
     @VisibleForTesting
+    @GuardedBy("mProcLock")
     LinkedHashMap<Integer, LastCompactionStats> mLastCompactionStats =
             new LinkedHashMap<Integer, LastCompactionStats>() {
                 @Override
@@ -264,6 +269,7 @@
     CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback,
             ProcessDependencies processDependencies) {
         mAm = am;
+        mProcLock = am.mProcLock;
         mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",
             Process.THREAD_GROUP_SYSTEM, true);
         mProcStateThrottle = new HashSet<>();
@@ -309,7 +315,7 @@
         }
     }
 
-    @GuardedBy("mAm")
+    @GuardedBy("mProcLock")
     void dump(PrintWriter pw) {
         pw.println("CachedAppOptimizer settings");
         synchronized (mPhenotypeFlagLock) {
@@ -350,58 +356,57 @@
         }
     }
 
-    @GuardedBy("mAm")
+    @GuardedBy("mProcLock")
     void compactAppSome(ProcessRecord app) {
-        app.reqCompactAction = COMPACT_PROCESS_SOME;
+        app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_SOME);
         mPendingCompactionProcesses.add(app);
         mCompactionHandler.sendMessage(
                 mCompactionHandler.obtainMessage(
-                COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
+                COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));
     }
 
-    @GuardedBy("mAm")
+    @GuardedBy("mProcLock")
     void compactAppFull(ProcessRecord app) {
-        app.reqCompactAction = COMPACT_PROCESS_FULL;
+        app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);
         mPendingCompactionProcesses.add(app);
         mCompactionHandler.sendMessage(
                 mCompactionHandler.obtainMessage(
-                COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
+                COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));
 
     }
 
-    @GuardedBy("mAm")
+    @GuardedBy("mProcLock")
     void compactAppPersistent(ProcessRecord app) {
-        app.reqCompactAction = COMPACT_PROCESS_PERSISTENT;
+        app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_PERSISTENT);
         mPendingCompactionProcesses.add(app);
         mCompactionHandler.sendMessage(
                 mCompactionHandler.obtainMessage(
-                    COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
+                    COMPACT_PROCESS_MSG, app.mState.getCurAdj(), app.mState.getSetProcState()));
     }
 
-    @GuardedBy("mAm")
+    @GuardedBy("mProcLock")
     boolean shouldCompactPersistent(ProcessRecord app, long now) {
-        return (app.lastCompactTime == 0
-                || (now - app.lastCompactTime) > mCompactThrottlePersistent);
+        return (app.mOptRecord.getLastCompactTime() == 0
+                || (now - app.mOptRecord.getLastCompactTime()) > mCompactThrottlePersistent);
     }
 
-    @GuardedBy("mAm")
+    @GuardedBy("mProcLock")
     void compactAppBfgs(ProcessRecord app) {
-        app.reqCompactAction = COMPACT_PROCESS_BFGS;
+        app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_BFGS);
         mPendingCompactionProcesses.add(app);
         mCompactionHandler.sendMessage(
                 mCompactionHandler.obtainMessage(
-                    COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
+                    COMPACT_PROCESS_MSG, app.mState.getCurAdj(), app.mState.getSetProcState()));
     }
 
-    @GuardedBy("mAm")
+    @GuardedBy("mProcLock")
     boolean shouldCompactBFGS(ProcessRecord app, long now) {
-        return (app.lastCompactTime == 0
-                || (now - app.lastCompactTime) > mCompactThrottleBFGS);
+        return (app.mOptRecord.getLastCompactTime() == 0
+                || (now - app.mOptRecord.getLastCompactTime()) > mCompactThrottleBFGS);
     }
 
-    @GuardedBy("mAm")
     void compactAllSystem() {
-        if (mUseCompaction) {
+        if (useCompaction()) {
             mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(
                                               COMPACT_SYSTEM_MSG));
         }
@@ -468,29 +473,28 @@
 
         // Override is applied immediately, restore is delayed
         synchronized (mAm) {
-            int processCount = mAm.mProcessList.mLruProcesses.size();
+            synchronized (mProcLock) {
+                mFreezerOverride = !enable;
+                Slog.d(TAG_AM, "freezer override set to " + mFreezerOverride);
 
-            mFreezerOverride = !enable;
-            Slog.d(TAG_AM, "freezer override set to " + mFreezerOverride);
+                mAm.mProcessList.forEachLruProcessesLOSP(true, process -> {
+                    if (process == null) {
+                        return;
+                    }
 
-            for (int i = 0; i < processCount; i++) {
-                ProcessRecord process = mAm.mProcessList.mLruProcesses.get(i);
+                    final ProcessCachedOptimizerRecord opt = process.mOptRecord;
+                    if (enable && opt.hasFreezerOverride()) {
+                        freezeAppAsyncLSP(process);
+                        opt.setFreezerOverride(false);
+                    }
 
-                if (process == null) {
-                    continue;
-                }
+                    if (!enable && opt.isFrozen()) {
+                        unfreezeAppLSP(process);
 
-                if (enable && process.freezerOverride) {
-                    freezeAppAsync(process);
-                    process.freezerOverride = false;
-                }
-
-                if (!enable && process.frozen) {
-                    unfreezeAppLocked(process);
-
-                    // Set freezerOverride *after* calling unfreezeAppLocked (it resets the flag)
-                    process.freezerOverride = true;
-                }
+                        // Set freezerOverride *after* calling unfreezeAppLSP (it resets the flag)
+                        opt.setFreezerOverride(true);
+                    }
+                });
             }
         }
 
@@ -740,19 +744,20 @@
     }
 
     // This will ensure app will be out of the freezer for at least FREEZE_TIMEOUT_MS
+    @GuardedBy("mAm")
     void unfreezeTemporarily(ProcessRecord app) {
         if (mUseFreezer) {
-            synchronized (mAm) {
-                if (app.frozen) {
-                    unfreezeAppLocked(app);
-                    freezeAppAsync(app);
+            synchronized (mProcLock) {
+                if (app.mOptRecord.isFrozen()) {
+                    unfreezeAppLSP(app);
+                    freezeAppAsyncLSP(app);
                 }
             }
         }
     }
 
-    @GuardedBy("mAm")
-    void freezeAppAsync(ProcessRecord app) {
+    @GuardedBy({"mAm", "mProcLock"})
+    void freezeAppAsyncLSP(ProcessRecord app) {
         mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
 
         mFreezeHandler.sendMessageDelayed(
@@ -761,16 +766,17 @@
                 FREEZE_TIMEOUT_MS);
     }
 
-    @GuardedBy("mAm")
-    void unfreezeAppLocked(ProcessRecord app) {
+    @GuardedBy({"mAm", "mProcLock"})
+    void unfreezeAppLSP(ProcessRecord app) {
         mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
 
-        app.freezerOverride = false;
-
-        if (!app.frozen) {
+        final int pid = app.getPid();
+        final ProcessCachedOptimizerRecord opt = app.mOptRecord;
+        opt.setFreezerOverride(false);
+        if (!opt.isFrozen()) {
             if (DEBUG_FREEZER) {
                 Slog.d(TAG_AM,
-                        "Skipping unfreeze for process " + app.pid + " "
+                        "Skipping unfreeze for process " + pid + " "
                         + app.processName + " (not frozen)");
             }
             return;
@@ -781,25 +787,25 @@
         boolean processKilled = false;
 
         try {
-            int freezeInfo = getBinderFreezeInfo(app.pid);
+            int freezeInfo = getBinderFreezeInfo(pid);
 
             if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
-                Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "
+                Slog.d(TAG_AM, "pid " + pid + " " + app.processName + " "
                         + " received sync transactions while frozen, killing");
-                app.kill("Sync transaction while in frozen state",
+                app.killLocked("Sync transaction while in frozen state",
                         ApplicationExitInfo.REASON_OTHER,
                         ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
                 processKilled = true;
             }
 
             if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0) {
-                Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "
+                Slog.d(TAG_AM, "pid " + pid + " " + app.processName + " "
                         + " received async transactions while frozen");
             }
         } catch (Exception e) {
-            Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + app.pid + " "
+            Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + pid + " "
                     + app.processName + ". Killing it. Exception: " + e);
-            app.kill("Unable to query binder frozen stats",
+            app.killLocked("Unable to query binder frozen stats",
                     ApplicationExitInfo.REASON_OTHER,
                     ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
             processKilled = true;
@@ -809,38 +815,38 @@
             return;
         }
 
-        long freezeTime = app.freezeUnfreezeTime;
+        long freezeTime = opt.getFreezeUnfreezeTime();
 
         try {
-            freezeBinder(app.pid, false);
+            freezeBinder(pid, false);
         } catch (RuntimeException e) {
-            Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName
+            Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName
                     + ". Killing it");
-            app.kill("Unable to unfreeze",
+            app.killLocked("Unable to unfreeze",
                     ApplicationExitInfo.REASON_OTHER,
                     ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
             return;
         }
 
         try {
-            Process.setProcessFrozen(app.pid, app.uid, false);
+            Process.setProcessFrozen(pid, app.uid, false);
 
-            app.freezeUnfreezeTime = SystemClock.uptimeMillis();
-            app.frozen = false;
+            opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
+            opt.setFrozen(false);
         } catch (Exception e) {
-            Slog.e(TAG_AM, "Unable to unfreeze " + app.pid + " " + app.processName
+            Slog.e(TAG_AM, "Unable to unfreeze " + pid + " " + app.processName
                     + ". This might cause inconsistency or UI hangs.");
         }
 
-        if (!app.frozen) {
+        if (!opt.isFrozen()) {
             if (DEBUG_FREEZER) {
-                Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName);
+                Slog.d(TAG_AM, "sync unfroze " + pid + " " + app.processName);
             }
 
             mFreezeHandler.sendMessage(
                     mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG,
-                        app.pid,
-                        (int) Math.min(app.freezeUnfreezeTime - freezeTime, Integer.MAX_VALUE),
+                        pid,
+                        (int) Math.min(opt.getFreezeUnfreezeTime() - freezeTime, Integer.MAX_VALUE),
                         app.processName));
         }
     }
@@ -869,6 +875,7 @@
                 case COMPACT_PROCESS_MSG: {
                     long start = SystemClock.uptimeMillis();
                     ProcessRecord proc;
+                    final ProcessCachedOptimizerRecord opt;
                     int pid;
                     String action;
                     final String name;
@@ -877,18 +884,19 @@
                     LastCompactionStats lastCompactionStats;
                     int lastOomAdj = msg.arg1;
                     int procState = msg.arg2;
-                    synchronized (mAm) {
+                    synchronized (mProcLock) {
                         proc = mPendingCompactionProcesses.remove(0);
+                        opt = proc.mOptRecord;
 
-                        pendingAction = proc.reqCompactAction;
-                        pid = proc.pid;
+                        pendingAction = opt.getReqCompactAction();
+                        pid = proc.getPid();
                         name = proc.processName;
 
                         // don't compact if the process has returned to perceptible
                         // and this is only a cached/home/prev compaction
                         if ((pendingAction == COMPACT_PROCESS_SOME
                                 || pendingAction == COMPACT_PROCESS_FULL)
-                                && (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
+                                && (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
                             if (DEBUG_COMPACTION) {
                                 Slog.d(TAG_AM,
                                         "Skipping compaction as process " + name + " is "
@@ -897,8 +905,8 @@
                             return;
                         }
 
-                        lastCompactAction = proc.lastCompactAction;
-                        lastCompactTime = proc.lastCompactTime;
+                        lastCompactAction = opt.getLastCompactAction();
+                        lastCompactTime = opt.getLastCompactTime();
                         lastCompactionStats = mLastCompactionStats.get(pid);
                     }
 
@@ -1076,9 +1084,9 @@
                                     lastOomAdj, ActivityManager.processStateAmToProto(procState),
                                     zramFreeKbBefore, zramFreeKbAfter);
                         }
-                        synchronized (mAm) {
-                            proc.lastCompactTime = end;
-                            proc.lastCompactAction = pendingAction;
+                        synchronized (mProcLock) {
+                            opt.setLastCompactTime(end);
+                            opt.setLastCompactAction(pendingAction);
                         }
                         if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])
                                 || action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
@@ -1126,11 +1134,12 @@
             }
         }
 
-        private void freezeProcess(ProcessRecord proc) {
-            final int pid = proc.pid;
+        private void freezeProcess(final ProcessRecord proc) {
+            int pid = proc.getPid(); // Unlocked intentionally
             final String name = proc.processName;
             final long unfrozenDuration;
             final boolean frozen;
+            final ProcessCachedOptimizerRecord opt = proc.mOptRecord;
 
             try {
                 // pre-check for locks to avoid unnecessary freeze/unfreeze operations
@@ -1146,26 +1155,27 @@
                 return;
             }
 
-            synchronized (mAm) {
-                if (proc.curAdj < ProcessList.CACHED_APP_MIN_ADJ
-                        || proc.shouldNotFreeze) {
+            synchronized (mProcLock) {
+                pid = proc.getPid();
+                if (proc.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ
+                        || opt.shouldNotFreeze()) {
                     if (DEBUG_FREEZER) {
                         Slog.d(TAG_AM, "Skipping freeze for process " + pid
-                                + " " + name + " curAdj = " + proc.curAdj
-                                + ", shouldNotFreeze = " + proc.shouldNotFreeze);
+                                + " " + name + " curAdj = " + proc.mState.getCurAdj()
+                                + ", shouldNotFreeze = " + opt.shouldNotFreeze());
                     }
                     return;
                 }
 
                 if (mFreezerOverride) {
-                    proc.freezerOverride = true;
+                    opt.setFreezerOverride(true);
                     Slog.d(TAG_AM, "Skipping freeze for process " + pid
-                            + " " + name + " curAdj = " + proc.curAdj
+                            + " " + name + " curAdj = " + proc.mState.getCurAdj()
                             + "(override)");
                     return;
                 }
 
-                if (pid == 0 || proc.frozen) {
+                if (pid == 0 || opt.isFrozen()) {
                     // Already frozen or not a real process, either one being
                     // launched or one being killed
                     return;
@@ -1177,24 +1187,28 @@
                     freezeBinder(pid, true);
                 } catch (RuntimeException e) {
                     Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
-                    proc.kill("Unable to freeze binder interface",
-                            ApplicationExitInfo.REASON_OTHER,
-                            ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+                    mFreezeHandler.post(() -> {
+                        synchronized (mAm) {
+                            proc.killLocked("Unable to freeze binder interface",
+                                    ApplicationExitInfo.REASON_OTHER,
+                                    ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+                        }
+                    });
                 }
 
-                long unfreezeTime = proc.freezeUnfreezeTime;
+                long unfreezeTime = opt.getFreezeUnfreezeTime();
 
                 try {
                     Process.setProcessFrozen(pid, proc.uid, true);
 
-                    proc.freezeUnfreezeTime = SystemClock.uptimeMillis();
-                    proc.frozen = true;
+                    opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
+                    opt.setFrozen(true);
                 } catch (Exception e) {
                     Slog.w(TAG_AM, "Unable to freeze " + pid + " " + name);
                 }
 
-                unfrozenDuration = proc.freezeUnfreezeTime - unfreezeTime;
-                frozen = proc.frozen;
+                unfrozenDuration = opt.getFreezeUnfreezeTime() - unfreezeTime;
+                frozen = opt.isFrozen();
             }
 
             if (!frozen) {
@@ -1225,13 +1239,17 @@
                     }
 
                     synchronized (mAm) {
-                        unfreezeAppLocked(proc);
+                        synchronized (mProcLock) {
+                            unfreezeAppLSP(proc);
+                        }
                     }
                 }
             } catch (IOException e) {
                 Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
                 synchronized (mAm) {
-                    unfreezeAppLocked(proc);
+                    synchronized (mProcLock) {
+                        unfreezeAppLSP(proc);
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 478c512..f43c7f6 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -57,6 +57,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -148,7 +149,7 @@
 
             ProcessRecord r = null;
             if (caller != null) {
-                r = mService.getRecordForAppLocked(caller);
+                r = mService.getRecordForAppLOSP(caller);
                 if (r == null) {
                     throw new SecurityException("Unable to find app for caller " + caller
                             + " (pid=" + Binder.getCallingPid() + ") when getting content provider "
@@ -183,14 +184,14 @@
 
             ProcessRecord dyingProc = null;
             if (cpr != null && cpr.proc != null) {
-                providerRunning = !cpr.proc.killed;
+                providerRunning = !cpr.proc.isKilled();
 
                 // Note if killedByAm is also set, this means the provider process has just been
                 // killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called
                 // yet. So we need to call appDiedLocked() here and let it clean up.
                 // (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
                 // how to test this case.)
-                if (cpr.proc.killed && cpr.proc.killedByAm) {
+                if (cpr.proc.isKilled() && cpr.proc.isKilledByAm()) {
                     Slog.wtf(TAG, cpr.proc.toString() + " was killed by AM but isn't really dead");
                     // Now we are going to wait for the death before starting the new process.
                     dyingProc = cpr.proc;
@@ -235,7 +236,7 @@
                             callingTag, stable, true, startTime, mService.mProcessList);
 
                     checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
-                    final int verifiedAdj = cpr.proc.verifiedAdj;
+                    final int verifiedAdj = cpr.proc.mState.getVerifiedAdj();
                     boolean success = mService.updateOomAdjLocked(cpr.proc, true,
                             OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
                     // XXX things have changed so updateOomAdjLocked doesn't actually tell us
@@ -243,7 +244,7 @@
                     // it, we will check whether the process still exists.  Note that this doesn't
                     // completely get rid of races with LMK killing the process, but should make
                     // them much smaller.
-                    if (success && verifiedAdj != cpr.proc.setAdj
+                    if (success && verifiedAdj != cpr.proc.mState.getSetAdj()
                             && !isProcessAliveLocked(cpr.proc)) {
                         success = false;
                     }
@@ -275,7 +276,7 @@
                         conn = null;
                         dyingProc = cpr.proc;
                     } else {
-                        cpr.proc.verifiedAdj = cpr.proc.setAdj;
+                        cpr.proc.mState.setVerifiedAdj(cpr.proc.mState.getSetAdj());
                     }
                 } finally {
                     Binder.restoreCallingIdentity(origId);
@@ -428,15 +429,18 @@
                         checkTime(startTime, "getContentProviderImpl: looking for process record");
                         ProcessRecord proc = mService.getProcessRecordLocked(
                                 cpi.processName, cpr.appInfo.uid, false);
-                        if (proc != null && proc.thread != null && !proc.killed) {
+                        IApplicationThread thread;
+                        if (proc != null && (thread = proc.getThread()) != null
+                                && !proc.isKilled()) {
                             if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
                                 Slog.d(TAG, "Installing in existing process " + proc);
                             }
-                            if (!proc.pubProviders.containsKey(cpi.name)) {
+                            final ProcessProviderRecord pr = proc.mProviders;
+                            if (!pr.hasProvider(cpi.name)) {
                                 checkTime(startTime, "getContentProviderImpl: scheduling install");
-                                proc.pubProviders.put(cpi.name, cpr);
+                                pr.installProvider(cpi.name, cpr);
                                 try {
-                                    proc.thread.scheduleInstallProvider(cpi);
+                                    thread.scheduleInstallProvider(cpi);
                                 } catch (RemoteException e) {
                                 }
                             }
@@ -445,8 +449,8 @@
                             proc = mService.startProcessLocked(
                                     cpi.processName, cpr.appInfo, false, 0,
                                     new HostingRecord("content provider",
-                                            new ComponentName(
-                                                    cpi.applicationInfo.packageName, cpi.name)),
+                                        new ComponentName(
+                                                cpi.applicationInfo.packageName, cpi.name)),
                                     Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
                             checkTime(startTime, "getContentProviderImpl: after start process");
                             if (proc == null) {
@@ -558,9 +562,9 @@
             // Note we do it after releasing the lock.
             String callerName = "unknown";
             if (caller != null) {
-                synchronized (mService) {
+                synchronized (mService.mProcLock) {
                     final ProcessRecord record =
-                            mService.mProcessList.getLRURecordForAppLocked(caller);
+                            mService.mProcessList.getLRURecordForAppLOSP(caller);
                     if (record != null) {
                         callerName = record.processName;
                     }
@@ -600,7 +604,7 @@
 
         mService.enforceNotIsolatedCaller("publishContentProviders");
         synchronized (mService) {
-            final ProcessRecord r = mService.getRecordForAppLocked(caller);
+            final ProcessRecord r = mService.getRecordForAppLOSP(caller);
             if (DEBUG_MU) {
                 Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
             }
@@ -617,7 +621,7 @@
                 if (src == null || src.info == null || src.provider == null) {
                     continue;
                 }
-                ContentProviderRecord dst = r.pubProviders.get(src.info.name);
+                ContentProviderRecord dst = r.mProviders.getProvider(src.info.name);
                 if (dst == null) {
                     continue;
                 }
@@ -808,7 +812,7 @@
             }
 
             ProcessRecord proc = conn.provider.proc;
-            if (proc == null || proc.thread == null) {
+            if (proc == null || proc.getThread() == null) {
                 // Seems like the process is already cleaned up.
                 return;
             }
@@ -816,7 +820,7 @@
             // As far as we're concerned, this is just like receiving a
             // death notification...  just a bit prematurely.
             mService.reportUidInfoMessageLocked(TAG, "Process " + proc.processName
-                            + " (pid " + proc.pid + ") early provider death", proc.info.uid);
+                            + " (pid " + proc.getPid() + ") early provider death", proc.info.uid);
             final long token = Binder.clearCallingIdentity();
             try {
                 mService.appDiedLocked(proc, "unstable content provider");
@@ -1074,7 +1078,8 @@
         }
 
         int numProviders = providers.size();
-        app.pubProviders.ensureCapacity(numProviders + app.pubProviders.size());
+        final ProcessProviderRecord pr = app.mProviders;
+        pr.ensureProviderCapacity(numProviders + pr.numberOfProviders());
         for (int i = 0; i < numProviders; i++) {
             // NOTE: keep logic in sync with installEncryptionUnawareProviders
             ProviderInfo cpi = providers.get(i);
@@ -1111,7 +1116,7 @@
             if (DEBUG_MU) {
                 Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
             }
-            app.pubProviders.put(cpi.name, cpr);
+            pr.installProvider(cpi.name, cpr);
             if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
                 // Don't add this if it is a platform component that is marked
                 // to run in multiple processes, because this is actually
@@ -1162,7 +1167,8 @@
     public final void installSystemProviders() {
         List<ProviderInfo> providers;
         synchronized (mService) {
-            ProcessRecord app = mService.mProcessList.mProcessNames.get("system", SYSTEM_UID);
+            ProcessRecord app = mService.mProcessList
+                    .getProcessNamesLOSP().get("system", SYSTEM_UID);
             providers = generateApplicationProvidersLocked(app);
             if (providers != null) {
                 for (int i = providers.size() - 1; i >= 0; i--) {
@@ -1205,25 +1211,29 @@
         final int matchFlags =
                 PackageManager.GET_PROVIDERS | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
-        synchronized (mService) {
-            final int numProc = mService.mProcessList.mProcessNames.getMap().size();
+        synchronized (mService.mProcLock) {
+            final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
+                    mService.mProcessList.getProcessNamesLOSP().getMap();
+            final int numProc = pmap.size();
             for (int iProc = 0; iProc < numProc; iProc++) {
-                final SparseArray<ProcessRecord> apps =
-                        mService.mProcessList.mProcessNames.getMap().valueAt(iProc);
+                final SparseArray<ProcessRecord> apps = pmap.valueAt(iProc);
                 for (int iApp = 0, numApps = apps.size(); iApp < numApps; iApp++) {
                     final ProcessRecord app = apps.valueAt(iApp);
-                    if (app.userId != userId || app.thread == null || app.unlocked) continue;
+                    if (app.userId != userId || app.getThread() == null || app.isUnlocked()) {
+                        continue;
+                    }
 
                     app.getPkgList().forEachPackage(pkgName -> {
                         try {
                             final PackageInfo pkgInfo = AppGlobals.getPackageManager()
-                                    .getPackageInfo(pkgName, matchFlags, userId);
+                                    .getPackageInfo(pkgName, matchFlags, app.userId);
+                            final IApplicationThread thread = app.getThread();
                             if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) {
                                 for (ProviderInfo pi : pkgInfo.providers) {
                                     // NOTE: keep in sync with generateApplicationProvidersLocked
                                     final boolean processMatch =
                                             Objects.equals(pi.processName, app.processName)
-                                                    || pi.multiprocess;
+                                            || pi.multiprocess;
                                     final boolean userMatch = !mService.isSingleton(
                                             pi.processName, pi.applicationInfo, pi.name, pi.flags)
                                             || app.userId == UserHandle.USER_SYSTEM;
@@ -1234,7 +1244,7 @@
                                     if (processMatch && userMatch
                                             && (!isInstantApp || splitInstalled)) {
                                         Log.v(TAG, "Installing " + pi);
-                                        app.thread.scheduleInstallProvider(pi);
+                                        thread.scheduleInstallProvider(pi);
                                     } else {
                                         Log.v(TAG, "Skipping " + pi);
                                     }
@@ -1259,8 +1269,9 @@
         }
 
 
-        for (int i = 0, size = r.conProviders.size(); i < size; i++) {
-            ContentProviderConnection conn = r.conProviders.get(i);
+        final ProcessProviderRecord pr = r.mProviders;
+        for (int i = 0, size = pr.numberOfProviderConnections(); i < size; i++) {
+            ContentProviderConnection conn = pr.getProviderConnectionAt(i);
             if (conn.provider == cpr) {
                 conn.incrementCount(stable);
                 return conn;
@@ -1272,10 +1283,11 @@
         conn.startAssociationIfNeeded();
         conn.initializeCount(stable);
         cpr.connections.add(conn);
-        r.conProviders.add(conn);
-        mService.startAssociationLocked(r.uid, r.processName, r.getCurProcState(),
+        pr.addProviderConnection(conn);
+        mService.startAssociationLocked(r.uid, r.processName, r.mState.getCurProcState(),
                 cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
-        if (updateLru && cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+        if (updateLru && cpr.proc != null
+                && r.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
             // If this is a perceptible app accessing the provider, make
             // sure to count it as being accessed and thus back up on
             // the LRU list.  This is good because content providers are
@@ -1325,13 +1337,14 @@
             final ContentProviderRecord cpr = conn.provider;
             conn.stopAssociation();
             cpr.connections.remove(conn);
-            conn.client.conProviders.remove(conn);
-            if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+            conn.client.mProviders.removeProviderConnection(conn);
+            if (conn.client.mState.getSetProcState()
+                    < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
                 // The client is more important than last activity -- note the time this
                 // is happening, so we keep the old provider process around a bit as last
                 // activity to avoid thrashing it.
                 if (cpr.proc != null) {
-                    cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+                    cpr.proc.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
                 }
             }
             mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
@@ -1429,9 +1442,9 @@
             return mService.validateAssociationAllowedLocked(cpi.packageName,
                     cpi.applicationInfo.uid, null, callingUid) ? null : "<null>";
         }
-        final String r = callingApp.getPkgList().forEachPackage(pkgName -> {
+        final String r = callingApp.getPkgList().searchEachPackage(pkgName -> {
             if (!mService.validateAssociationAllowedLocked(pkgName,
-                    callingApp.uid, cpi.packageName, cpi.applicationInfo.uid)) {
+                        callingApp.uid, cpi.packageName, cpi.applicationInfo.uid)) {
                 return cpi.packageName;
             }
             return null;
@@ -1455,8 +1468,8 @@
 
     private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
             String authority) {
-        if (app == null
-                || app.getCurProcState() > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+        if (app == null || app.mState.getCurProcState()
+                > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
             return;
         }
 
@@ -1483,31 +1496,30 @@
     private final long[] mProcessStateStatsLongs = new long[1];
 
     private boolean isProcessAliveLocked(ProcessRecord proc) {
-        if (proc.pid <= 0) {
+        final int pid = proc.getPid();
+        if (pid <= 0) {
             if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
                 Slog.d(ActivityManagerService.TAG, "Process hasn't started yet: " + proc);
             }
             return false;
         }
-        if (proc.procStatFile == null) {
-            proc.procStatFile = "/proc/" + proc.pid + "/stat";
-        }
+        final String procStatFile = "/proc/" + pid + "/stat";
         mProcessStateStatsLongs[0] = 0;
-        if (!Process.readProcFile(proc.procStatFile, PROCESS_STATE_STATS_FORMAT, null,
+        if (!Process.readProcFile(procStatFile, PROCESS_STATE_STATS_FORMAT, null,
                 mProcessStateStatsLongs, null)) {
             if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
                 Slog.d(ActivityManagerService.TAG,
-                        "UNABLE TO RETRIEVE STATE FOR " + proc.procStatFile);
+                        "UNABLE TO RETRIEVE STATE FOR " + procStatFile);
             }
             return false;
         }
         final long state = mProcessStateStatsLongs[0];
         if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
             Slog.d(ActivityManagerService.TAG,
-                    "RETRIEVED STATE FOR " + proc.procStatFile + ": " + (char) state);
+                    "RETRIEVED STATE FOR " + procStatFile + ": " + (char) state);
         }
         if (state != 'Z' && state != 'X' && state != 'x' && state != 'K') {
-            return Process.getUidForPid(proc.pid) == proc.uid;
+            return Process.getUidForPid(pid) == proc.uid;
         }
         return false;
     }
@@ -1537,7 +1549,7 @@
         }
 
         final boolean callerForeground = r == null
-                || r.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
+                || r.mState.getSetSchedGroup() != ProcessList.SCHED_GROUP_BACKGROUND;
 
         // Show a permission review UI only for starting from a foreground app
         if (!callerForeground) {
@@ -1611,26 +1623,29 @@
                 }
             }
             ProcessRecord capp = conn.client;
+            final IApplicationThread thread = capp.getThread();
             conn.dead = true;
             if (conn.stableCount() > 0) {
-                if (!capp.isPersistent() && capp.thread != null
-                        && capp.pid != 0 && capp.pid != ActivityManagerService.MY_PID) {
-                    capp.kill("depends on provider " + cpr.name.flattenToShortString()
-                                    + " in dying proc " + (proc != null ? proc.processName : "??")
-                                    + " (adj " + (proc != null ? proc.setAdj : "??") + ")",
+                final int pid = capp.getPid();
+                if (!capp.isPersistent() && thread != null
+                        && pid != 0 && pid != ActivityManagerService.MY_PID) {
+                    capp.killLocked(
+                            "depends on provider " + cpr.name.flattenToShortString()
+                            + " in dying proc " + (proc != null ? proc.processName : "??")
+                            + " (adj " + (proc != null ? proc.mState.getSetAdj() : "??") + ")",
                             ApplicationExitInfo.REASON_DEPENDENCY_DIED,
                             ApplicationExitInfo.SUBREASON_UNKNOWN,
                             true);
                 }
-            } else if (capp.thread != null && conn.provider.provider != null) {
+            } else if (thread != null && conn.provider.provider != null) {
                 try {
-                    capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
+                    thread.unstableProviderDied(conn.provider.provider.asBinder());
                 } catch (RemoteException e) {
                 }
                 // In the protocol here, we don't expect the client to correctly
                 // clean up this connection, we'll just remove it.
                 cpr.connections.remove(i);
-                if (conn.client.conProviders.remove(conn)) {
+                if (conn.client.mProviders.removeProviderConnection(conn)) {
                     mService.stopAssociationLocked(capp.uid, capp.processName,
                             cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
                 }
@@ -1671,7 +1686,7 @@
                 // It's being launched but we've reached maximum attempts, mark it as bad
                 alwaysBad = true;
             }
-            if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
+            if (!alwaysBad && !app.mErrorState.isBad() && cpr.hasConnectionOrHandle()) {
                 restart = true;
             } else {
                 removeDyingProviderLocked(app, cpr, true);
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index 59a1939..b217cae 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -19,6 +19,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 
 import android.app.ContentProviderHolder;
+import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.IContentProvider;
 import android.content.pm.ApplicationInfo;
@@ -211,9 +212,10 @@
                                 + " caller=" + client);
                     }
                 }
-                if (client.thread != null) {
+                final IApplicationThread thread = client.getThread();
+                if (thread != null) {
                     try {
-                        client.thread.notifyContentProviderPublishStatus(
+                        thread.notifyContentProviderPublishStatus(
                                 newHolder(status ? conn : null, false),
                                 info.authority, userId, status);
                     } catch (RemoteException e) {
@@ -343,8 +345,8 @@
                     && mAssociation == null && provider.proc != null
                     && (provider.appInfo.uid != mOwningUid
                             || !provider.info.processName.equals(mOwningProcessName))) {
-                ProcessStats.ProcessStateHolder holder = provider.proc.getPkgList().get(
-                        provider.name.getPackageName());
+                ProcessStats.ProcessStateHolder holder =
+                        provider.proc.getPkgList().get(provider.name.getPackageName());
                 if (holder == null) {
                     Slog.wtf(TAG_AM, "No package in referenced provider "
                             + provider.name.toShortString() + ": proc=" + provider.proc);
@@ -373,7 +375,7 @@
                 if (hasExternalProcessHandles() &&
                         externalProcessTokenToHandle.get(mToken) != null) {
                     removeExternalProcessHandleInternalLocked(mToken);
-                }                        
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ErrorDialogController.java b/services/core/java/com/android/server/am/ErrorDialogController.java
new file mode 100644
index 0000000..ef135d5
--- /dev/null
+++ b/services/core/java/com/android/server/am/ErrorDialogController.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2020 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 android.app.Dialog;
+import android.content.Context;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A controller to generate error dialogs in {@link ProcessRecord}.
+ */
+final class ErrorDialogController {
+    private final ProcessRecord mApp;
+    private final ActivityManagerService mService;
+    private final ActivityManagerGlobalLock mProcLock;
+
+    /**
+     * Dialogs being displayed due to crash.
+     */
+    @GuardedBy("mProcLock")
+    private List<AppErrorDialog> mCrashDialogs;
+
+    /**
+     * Dialogs being displayed due to app not responding.
+     */
+    @GuardedBy("mProcLock")
+    private List<AppNotRespondingDialog> mAnrDialogs;
+
+    /**
+     * Dialogs displayed due to strict mode violation.
+     */
+    @GuardedBy("mProcLock")
+    private List<StrictModeViolationDialog> mViolationDialogs;
+
+    /**
+     * Current wait for debugger dialog.
+     */
+    @GuardedBy("mProcLock")
+    private AppWaitingForDebuggerDialog mWaitDialog;
+
+    @GuardedBy("mProcLock")
+    boolean hasCrashDialogs() {
+        return mCrashDialogs != null;
+    }
+
+    @GuardedBy("mProcLock")
+    List<AppErrorDialog> getCrashDialogs() {
+        return mCrashDialogs;
+    }
+
+    @GuardedBy("mProcLock")
+    boolean hasAnrDialogs() {
+        return mAnrDialogs != null;
+    }
+
+    @GuardedBy("mProcLock")
+    List<AppNotRespondingDialog> getAnrDialogs() {
+        return mAnrDialogs;
+    }
+
+    @GuardedBy("mProcLock")
+    boolean hasViolationDialogs() {
+        return mViolationDialogs != null;
+    }
+
+    @GuardedBy("mProcLock")
+    boolean hasDebugWaitingDialog() {
+        return mWaitDialog != null;
+    }
+
+    @GuardedBy("mProcLock")
+    void clearAllErrorDialogs() {
+        clearCrashDialogs();
+        clearAnrDialogs();
+        clearViolationDialogs();
+        clearWaitingDialog();
+    }
+
+    @GuardedBy("mProcLock")
+    void clearCrashDialogs() {
+        clearCrashDialogs(true /* needDismiss */);
+    }
+
+    @GuardedBy("mProcLock")
+    void clearCrashDialogs(boolean needDismiss) {
+        if (mCrashDialogs == null) {
+            return;
+        }
+        if (needDismiss) {
+            forAllDialogs(mCrashDialogs, Dialog::dismiss);
+        }
+        mCrashDialogs = null;
+    }
+
+    @GuardedBy("mProcLock")
+    void clearAnrDialogs() {
+        if (mAnrDialogs == null) {
+            return;
+        }
+        forAllDialogs(mAnrDialogs, Dialog::dismiss);
+        mAnrDialogs = null;
+    }
+
+    @GuardedBy("mProcLock")
+    void clearViolationDialogs() {
+        if (mViolationDialogs == null) {
+            return;
+        }
+        forAllDialogs(mViolationDialogs, Dialog::dismiss);
+        mViolationDialogs = null;
+    }
+
+    @GuardedBy("mProcLock")
+    void clearWaitingDialog() {
+        if (mWaitDialog == null) {
+            return;
+        }
+        mWaitDialog.dismiss();
+        mWaitDialog = null;
+    }
+
+    void forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c) {
+        for (int i = dialogs.size() - 1; i >= 0; i--) {
+            c.accept(dialogs.get(i));
+        }
+    }
+
+    @GuardedBy("mProcLock")
+    void showCrashDialogs(AppErrorDialog.Data data) {
+        List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
+        mCrashDialogs = new ArrayList<>();
+        for (int i = contexts.size() - 1; i >= 0; i--) {
+            final Context c = contexts.get(i);
+            mCrashDialogs.add(new AppErrorDialog(c, mService, data));
+        }
+        mService.mUiHandler.post(() -> {
+            List<AppErrorDialog> dialogs;
+            synchronized (mProcLock) {
+                dialogs = mCrashDialogs;
+            }
+            if (dialogs != null) {
+                forAllDialogs(dialogs, Dialog::show);
+            }
+        });
+    }
+
+    @GuardedBy("mProcLock")
+    void showAnrDialogs(AppNotRespondingDialog.Data data) {
+        List<Context> contexts = getDisplayContexts(
+                mApp.mErrorState.isSilentAnr() /* lastUsedOnly */);
+        mAnrDialogs = new ArrayList<>();
+        for (int i = contexts.size() - 1; i >= 0; i--) {
+            final Context c = contexts.get(i);
+            mAnrDialogs.add(new AppNotRespondingDialog(mService, c, data));
+        }
+        mService.mUiHandler.post(() -> {
+            List<AppNotRespondingDialog> dialogs;
+            synchronized (mProcLock) {
+                dialogs = mAnrDialogs;
+            }
+            if (dialogs != null) {
+                forAllDialogs(dialogs, Dialog::show);
+            }
+        });
+    }
+
+    @GuardedBy("mProcLock")
+    void showViolationDialogs(AppErrorResult res) {
+        List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
+        mViolationDialogs = new ArrayList<>();
+        for (int i = contexts.size() - 1; i >= 0; i--) {
+            final Context c = contexts.get(i);
+            mViolationDialogs.add(
+                    new StrictModeViolationDialog(c, mService, res, mApp));
+        }
+        mService.mUiHandler.post(() -> {
+            List<StrictModeViolationDialog> dialogs;
+            synchronized (mProcLock) {
+                dialogs = mViolationDialogs;
+            }
+            if (dialogs != null) {
+                forAllDialogs(dialogs, Dialog::show);
+            }
+        });
+    }
+
+    @GuardedBy("mProcLock")
+    void showDebugWaitingDialogs() {
+        List<Context> contexts = getDisplayContexts(true /* lastUsedOnly */);
+        final Context c = contexts.get(0);
+        mWaitDialog = new AppWaitingForDebuggerDialog(mService, c, mApp);
+
+        mService.mUiHandler.post(() -> {
+            Dialog dialog;
+            synchronized (mProcLock) {
+                dialog = mWaitDialog;
+            }
+            if (dialog != null) {
+                dialog.show();
+            }
+        });
+    }
+
+    /**
+     * Helper function to collect contexts from crashed app located displays.
+     *
+     * @param lastUsedOnly Sets to {@code true} to indicate to only get last used context.
+     *                     Sets to {@code false} to collect contexts from crashed app located
+     *                     displays.
+     *
+     * @return display context list.
+     */
+    private List<Context> getDisplayContexts(boolean lastUsedOnly) {
+        List<Context> displayContexts = new ArrayList<>();
+        if (!lastUsedOnly) {
+            mApp.getWindowProcessController().getDisplayContextsWithErrorDialogs(displayContexts);
+        }
+        // If there is no foreground window display, fallback to last used display.
+        if (displayContexts.isEmpty() || lastUsedOnly) {
+            displayContexts.add(mService.mWmInternal != null
+                    ? mService.mWmInternal.getTopFocusedDisplayUiContext()
+                    : mService.mUiContext);
+        }
+        return displayContexts;
+    }
+
+    ErrorDialogController(ProcessRecord app) {
+        mApp = app;
+        mService = app.mService;
+        mProcLock = mService.mProcLock;
+    }
+}
diff --git a/services/core/java/com/android/server/am/NativeCrashListener.java b/services/core/java/com/android/server/am/NativeCrashListener.java
index 6e42aee..94eb076 100644
--- a/services/core/java/com/android/server/am/NativeCrashListener.java
+++ b/services/core/java/com/android/server/am/NativeCrashListener.java
@@ -16,6 +16,12 @@
 
 package com.android.server.am;
 
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.SOCK_STREAM;
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_RCVTIMEO;
+import static android.system.OsConstants.SO_SNDTIMEO;
+
 import android.app.ApplicationErrorReport.CrashInfo;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -23,8 +29,6 @@
 import android.system.UnixSocketAddress;
 import android.util.Slog;
 
-import static android.system.OsConstants.*;
-
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
@@ -259,8 +263,10 @@
                     // even though the process will vanish as soon as we let
                     // debuggerd proceed.
                     synchronized (mAm) {
-                        pr.setCrashing(true);
-                        pr.forceCrashReport = true;
+                        synchronized (mAm.mProcLock) {
+                            pr.mErrorState.setCrashing(true);
+                            pr.mErrorState.setForceCrashReport(true);
+                        }
                     }
 
                     // Crash reporting is synchronous but we want to let debuggerd
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 5e146e1..d79fb8a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -51,6 +51,7 @@
 import static android.os.Process.setThreadPriority;
 import static android.os.Process.setThreadScheduler;
 
+import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
@@ -99,6 +100,7 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.CompositeRWLock;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.compat.IPlatformCompat;
@@ -205,11 +207,12 @@
     int mNumCachedHiddenProcs = 0;
 
     /** Track all uids that have actively running processes. */
+    @CompositeRWLock({"mService", "mProcLock"})
     ActiveUids mActiveUids;
 
     /**
      * The handler to execute {@link #setProcessGroup} (it may be heavy if the process has many
-     * threads) for reducing the time spent in {@link #applyOomAdjLocked}.
+     * threads) for reducing the time spent in {@link #applyOomAdjLSP}.
      */
     private final Handler mProcessGroupHandler;
 
@@ -217,6 +220,7 @@
 
     private final ActivityManagerService mService;
     private final ProcessList mProcessList;
+    private final ActivityManagerGlobalLock mProcLock;
 
     private final int mNumSlots;
     private final ArrayList<ProcessRecord> mTmpProcessList = new ArrayList<ProcessRecord>();
@@ -332,6 +336,7 @@
             ServiceThread adjusterThread) {
         mService = service;
         mProcessList = processList;
+        mProcLock = service.mProcLock;
         mActiveUids = activeUids;
 
         mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
@@ -388,23 +393,27 @@
     @VisibleForTesting
     @GuardedBy("mService")
     void handleUserSwitchedLocked() {
+        mProcessList.forEachLruProcessesLOSP(false,
+                this::updateKeepWarmIfNecessaryForProcessLocked);
+    }
+
+    @GuardedBy("mService")
+    private void updateKeepWarmIfNecessaryForProcessLocked(final ProcessRecord app) {
         final ArraySet<ComponentName> warmServices = mService.mConstants.KEEP_WARMING_SERVICES;
-        final ArrayList<ProcessRecord> processes = mProcessList.mLruProcesses;
-        for (int i = processes.size() - 1; i >= 0; i--) {
-            final ProcessRecord app = processes.get(i);
-            boolean includeWarmPkg = false;
-            for (int j = warmServices.size() - 1; j >= 0; j--) {
-                if (app.getPkgList().containsKey(warmServices.valueAt(j).getPackageName())) {
-                    includeWarmPkg = true;
-                    break;
-                }
+        boolean includeWarmPkg = false;
+        final PackageList pkgList = app.getPkgList();
+        for (int j = warmServices.size() - 1; j >= 0; j--) {
+            if (pkgList.containsKey(warmServices.valueAt(j).getPackageName())) {
+                includeWarmPkg = true;
+                break;
             }
-            if (!includeWarmPkg) {
-                continue;
-            }
-            for (int j = app.numberOfRunningServices() - 1; j >= 0; j--) {
-                app.getRunningServiceAt(j).updateKeepWarmLocked();
-            }
+        }
+        if (!includeWarmPkg) {
+            return;
+        }
+        final ProcessServiceRecord psr = app.mServices;
+        for (int j = psr.numberOfRunningServices() - 1; j >= 0; j--) {
+            psr.getRunningServiceAt(j).updateKeepWarmLocked();
         }
     }
 
@@ -419,11 +428,20 @@
     @GuardedBy("mService")
     boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll,
             String oomAdjReason) {
+        synchronized (mProcLock) {
+            return updateOomAdjLSP(app, oomAdjAll, oomAdjReason);
+        }
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    private boolean updateOomAdjLSP(ProcessRecord app, boolean oomAdjAll,
+            String oomAdjReason) {
         if (oomAdjAll && mConstants.OOMADJ_UPDATE_QUICK) {
-            return updateOomAdjLocked(app, oomAdjReason);
+            return updateOomAdjLSP(app, oomAdjReason);
         }
         final ProcessRecord topApp = mService.getTopApp();
-        final boolean wasCached = app.isCached();
+        final ProcessStateRecord state = app.mState;
+        final boolean wasCached = state.isCached();
 
         mAdjSeq++;
 
@@ -431,30 +449,31 @@
         // If our app is currently cached, we know it, and that is it.  Otherwise,
         // we don't know it yet, and it needs to now be cached we will then
         // need to do a complete oom adj.
-        final int cachedAdj = app.getCurRawAdj() >= ProcessList.CACHED_APP_MIN_ADJ
-                ? app.getCurRawAdj() : ProcessList.UNKNOWN_ADJ;
+        final int cachedAdj = state.getCurRawAdj() >= ProcessList.CACHED_APP_MIN_ADJ
+                ? state.getCurRawAdj() : ProcessList.UNKNOWN_ADJ;
         // Check if this process is in the pending list too, remove from pending list if so.
         mPendingProcessSet.remove(app);
-        boolean success = updateOomAdjLocked(app, cachedAdj, topApp, false,
+        boolean success = updateOomAdjLSP(app, cachedAdj, topApp, false,
                 SystemClock.uptimeMillis());
         if (oomAdjAll
-                && (wasCached != app.isCached() || app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
+                && (wasCached != state.isCached()
+                    || state.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
             // Changed to/from cached state, so apps after it in the LRU
             // list may also be changed.
-            updateOomAdjLocked(oomAdjReason);
+            updateOomAdjLSP(oomAdjReason);
         }
         return success;
     }
 
-    @GuardedBy("mService")
-    private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
+    @GuardedBy({"mService", "mProcLock"})
+    private boolean updateOomAdjLSP(ProcessRecord app, int cachedAdj,
             ProcessRecord TOP_APP, boolean doingAll, long now) {
-        if (app.thread == null) {
+        if (app.getThread() == null) {
             return false;
         }
 
-        app.resetCachedInfo();
-        UidRecord uidRec = app.uidRecord;
+        app.mState.resetCachedInfo();
+        UidRecord uidRec = app.getUidRecord();
         if (uidRec != null) {
             if (DEBUG_UID_OBSERVERS) {
                 Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
@@ -465,35 +484,23 @@
         // Check if this process is in the pending list too, remove from pending list if so.
         mPendingProcessSet.remove(app);
 
-        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false, true);
+        computeOomAdjLSP(app, cachedAdj, TOP_APP, doingAll, now, false, true);
 
-        boolean success = applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
+        boolean success = applyOomAdjLSP(app, doingAll, now, SystemClock.elapsedRealtime());
 
         if (uidRec != null) {
-            // After uidRec.reset() above, for UidRecord that has multiple processes (ProcessRecord)
-            // , We need to apply all ProcessRecord into UidRecord.
-            final ArraySet<ProcessRecord> procRecords = app.uidRecord.procRecords;
-            for (int i = procRecords.size() - 1; i >= 0; i--) {
-                final ProcessRecord pr = procRecords.valueAt(i);
-                if (!pr.killedByAm && pr.thread != null) {
-                    if (pr.isolated && pr.numberOfRunningServices() <= 0
-                            && pr.isolatedEntryPoint == null) {
-                        // No op.
-                    } else {
-                        // Keeping this process, update its uid.
-                        updateAppUidRecLocked(pr);
-                    }
-                }
-            }
+            // After uidRec.reset() above, for UidRecord with multiple processes (ProcessRecord),
+            // we need to apply all ProcessRecord into UidRecord.
+            uidRec.forEachProcess(this::updateAppUidRecIfNecessaryLSP);
             if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
-                    && (uidRec.setProcState != uidRec.getCurProcState()
-                    || uidRec.setCapability != uidRec.curCapability
-                    || uidRec.mSetAllowlist != uidRec.mCurAllowlist)) {
+                    && (uidRec.getSetProcState() != uidRec.getCurProcState()
+                    || uidRec.getSetCapability() != uidRec.getCurCapability()
+                    || uidRec.isSetAllowListed() != uidRec.isCurAllowListed())) {
                 ActiveUids uids = mTmpUidRecords;
                 uids.clear();
-                uids.put(uidRec.uid, uidRec);
-                updateUidsLocked(uids, SystemClock.elapsedRealtime());
-                mProcessList.incrementProcStateSeqAndNotifyAppsLocked(uids);
+                uids.put(uidRec.getUid(), uidRec);
+                updateUidsLSP(uids, SystemClock.elapsedRealtime());
+                mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(uids);
             }
         }
 
@@ -505,11 +512,18 @@
      */
     @GuardedBy("mService")
     void updateOomAdjLocked(String oomAdjReason) {
+        synchronized (mProcLock) {
+            updateOomAdjLSP(oomAdjReason);
+        }
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    private void updateOomAdjLSP(String 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;
-        updateOomAdjLockedInner(oomAdjReason, topApp , null, null, true, true);
+        updateOomAdjInnerLSP(oomAdjReason, topApp , null, null, true, true);
     }
 
     /**
@@ -522,8 +536,15 @@
      */
     @GuardedBy("mService")
     boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
+        synchronized (mProcLock) {
+            return updateOomAdjLSP(app, oomAdjReason);
+        }
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    private boolean updateOomAdjLSP(ProcessRecord app, String oomAdjReason) {
         if (app == null || !mConstants.OOMADJ_UPDATE_QUICK) {
-            updateOomAdjLocked(oomAdjReason);
+            updateOomAdjLSP(oomAdjReason);
             return true;
         }
 
@@ -534,20 +555,23 @@
         mAdjSeq++;
 
         // Firstly, try to see if the importance of itself gets changed
-        final boolean wasCached = app.isCached();
-        final int oldAdj = app.getCurRawAdj();
+        final ProcessStateRecord state = app.mState;
+        final boolean wasCached = state.isCached();
+        final int oldAdj = state.getCurRawAdj();
         final int cachedAdj = oldAdj >= ProcessList.CACHED_APP_MIN_ADJ
                 ? oldAdj : ProcessList.UNKNOWN_ADJ;
-        final boolean wasBackground = ActivityManager.isProcStateBackground(app.setProcState);
-        app.containsCycle = false;
-        app.procStateChanged = false;
-        app.resetCachedInfo();
+        final boolean wasBackground = ActivityManager.isProcStateBackground(
+                state.getSetProcState());
+        state.setContainsCycle(false);
+        state.setProcStateChanged(false);
+        state.resetCachedInfo();
         // Check if this process is in the pending list too, remove from pending list if so.
         mPendingProcessSet.remove(app);
-        boolean success = updateOomAdjLocked(app, cachedAdj, topApp, false,
+        boolean success = updateOomAdjLSP(app, cachedAdj, topApp, false,
                 SystemClock.uptimeMillis());
-        if (!success || (wasCached == app.isCached() && oldAdj != ProcessList.INVALID_ADJ
-                && wasBackground == ActivityManager.isProcStateBackground(app.setProcState))) {
+        if (!success || (wasCached == state.isCached() && oldAdj != ProcessList.INVALID_ADJ
+                && wasBackground == ActivityManager.isProcStateBackground(
+                        state.getSetProcState()))) {
             // Okay, it's unchanged, it won't impact any service it binds to, we're done here.
             if (DEBUG_OOM_ADJ) {
                 Slog.i(TAG_OOM_ADJ, "No oomadj changes for " + app);
@@ -569,23 +593,25 @@
         // Track if any of them reachables could include a cycle
         boolean containsCycle = false;
         // Scan downstreams of the process record
-        app.mReachable = true;
+        state.setReachable(true);
         for (ProcessRecord pr = app; pr != null; pr = queue.poll()) {
             if (pr != app) {
                 processes.add(pr);
             }
-            if (pr.uidRecord != null) {
-                uids.put(pr.uidRecord.uid, pr.uidRecord);
+            final UidRecord uidRec = pr.getUidRecord();
+            if (uidRec != null) {
+                uids.put(uidRec.getUid(), uidRec);
             }
-            for (int i = pr.connections.size() - 1; i >= 0; i--) {
-                ConnectionRecord cr = pr.connections.valueAt(i);
+            final ProcessServiceRecord psr = pr.mServices;
+            for (int i = psr.numberOfConnections() - 1; i >= 0; i--) {
+                ConnectionRecord cr = psr.getConnectionAt(i);
                 ProcessRecord service = (cr.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
                         ? cr.binding.service.isolatedProc : cr.binding.service.app;
                 if (service == null || service == pr) {
                     continue;
                 }
-                containsCycle |= service.mReachable;
-                if (service.mReachable) {
+                containsCycle |= service.mState.isReachable();
+                if (service.mState.isReachable()) {
                     continue;
                 }
                 if ((cr.flags & (Context.BIND_WAIVE_PRIORITY
@@ -595,24 +621,26 @@
                     continue;
                 }
                 queue.offer(service);
-                service.mReachable = true;
+                service.mState.setReachable(true);
                 // During scanning the reachable dependants, remove them from the pending oomadj
                 // targets list if it's possible, as they've been added into the immediate
                 // oomadj targets list 'processes' above.
                 mPendingProcessSet.remove(service);
             }
-            for (int i = pr.conProviders.size() - 1; i >= 0; i--) {
-                ContentProviderConnection cpc = pr.conProviders.get(i);
+            final ProcessProviderRecord ppr = pr.mProviders;
+            for (int i = ppr.numberOfProviderConnections() - 1; i >= 0; i--) {
+                ContentProviderConnection cpc = ppr.getProviderConnectionAt(i);
                 ProcessRecord provider = cpc.provider.proc;
-                if (provider == null || provider == pr || (containsCycle |= provider.mReachable)) {
+                if (provider == null || provider == pr
+                        || (containsCycle |= provider.mState.isReachable())) {
                     continue;
                 }
-                containsCycle |= provider.mReachable;
-                if (provider.mReachable) {
+                containsCycle |= provider.mState.isReachable();
+                if (provider.mState.isReachable()) {
                     continue;
                 }
                 queue.offer(provider);
-                provider.mReachable = true;
+                provider.mState.setReachable(true);
                 // During scanning the reachable dependants, remove them from the pending oomadj
                 // targets list if it's possible, as they've been added into the immediate
                 // oomadj targets list 'processes' above.
@@ -621,10 +649,10 @@
         }
 
         // Reset the flag
-        app.mReachable = false;
+        state.setReachable(false);
         int size = processes.size();
         if (size > 0) {
-            // Reverse the process list, since the updateOomAdjLockedInner scans from the end of it.
+            // Reverse the process list, since the updateOomAdjInnerLSP scans from the end of it.
             for (int l = 0, r = size - 1; l < r; l++, r--) {
                 ProcessRecord t = processes.get(l);
                 processes.set(l, processes.get(r));
@@ -632,13 +660,13 @@
             }
             mAdjSeq--;
             // Update these reachable processes
-            updateOomAdjLockedInner(oomAdjReason, topApp, processes, uids, containsCycle, false);
-        } else if (app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ) {
+            updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, containsCycle, false);
+        } else if (state.getCurRawAdj() == ProcessList.UNKNOWN_ADJ) {
             // In case the app goes from non-cached to cached but it doesn't have other reachable
             // processes, its adj could be still unknown as of now, assign one.
             processes.add(app);
             assignCachedAdjIfNecessary(processes);
-            applyOomAdjLocked(app, false, SystemClock.uptimeMillis(),
+            applyOomAdjLSP(app, false, SystemClock.uptimeMillis(),
                     SystemClock.elapsedRealtime());
         }
         mTmpProcessList.clear();
@@ -683,17 +711,20 @@
 
         final ArrayList<ProcessRecord> processes = mTmpProcessList;
         final ActiveUids uids = mTmpUidRecords;
-        uids.clear();
-        processes.clear();
-        for (int i = mPendingProcessSet.size() - 1; i >= 0; i--) {
-            final ProcessRecord app = mPendingProcessSet.valueAt(i);
-            if (app.uidRecord != null) {
-                uids.put(app.uidRecord.uid, app.uidRecord);
+        synchronized (mProcLock) {
+            uids.clear();
+            processes.clear();
+            for (int i = mPendingProcessSet.size() - 1; i >= 0; i--) {
+                final ProcessRecord app = mPendingProcessSet.valueAt(i);
+                final UidRecord uidRec = app.getUidRecord();
+                if (uidRec != null) {
+                    uids.put(uidRec.getUid(), uidRec);
+                }
+                processes.add(app);
             }
-            processes.add(app);
-        }
 
-        updateOomAdjLockedInner(oomAdjReason, topApp, processes, uids, true, false);
+            updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, true, false);
+        }
         processes.clear();
         mPendingProcessSet.clear();
 
@@ -706,8 +737,8 @@
      * list if the given list is null; when it's partial update, each process's client proc won't
      * get evaluated recursively here.
      */
-    @GuardedBy("mService")
-    private void updateOomAdjLockedInner(String oomAdjReason, final ProcessRecord topApp,
+    @GuardedBy({"mService", "mProcLock"})
+    private void updateOomAdjInnerLSP(String oomAdjReason, final ProcessRecord topApp,
             ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
             boolean startProfiling) {
         if (startProfiling) {
@@ -719,7 +750,7 @@
         final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
         final boolean fullUpdate = processes == null;
         ActiveUids activeUids = uids;
-        ArrayList<ProcessRecord> activeProcesses = fullUpdate ? mProcessList.mLruProcesses
+        ArrayList<ProcessRecord> activeProcesses = fullUpdate ? mProcessList.getLruProcessesLOSP()
                 : processes;
         final int numProc = activeProcesses.size();
 
@@ -728,8 +759,8 @@
             activeUids = mTmpUidRecords;
             activeUids.clear();
             for (int i = 0; i < numUids; i++) {
-                UidRecord r = mActiveUids.valueAt(i);
-                activeUids.put(r.uid, r);
+                UidRecord uidRec = mActiveUids.valueAt(i);
+                activeUids.put(uidRec.getUid(), uidRec);
             }
         }
 
@@ -751,36 +782,39 @@
         boolean retryCycles = false;
         boolean computeClients = fullUpdate || potentialCycles;
 
-        // need to reset cycle state before calling computeOomAdjLocked because of service conns
+        // need to reset cycle state before calling computeOomAdjLSP because of service conns
         for (int i = numProc - 1; i >= 0; i--) {
             ProcessRecord app = activeProcesses.get(i);
-            app.mReachable = false;
+            final ProcessStateRecord state = app.mState;
+            state.setReachable(false);
             // No need to compute again it has been evaluated in previous iteration
-            if (app.adjSeq != mAdjSeq) {
-                app.containsCycle = false;
-                app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
-                app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
-                app.setCapability = PROCESS_CAPABILITY_NONE;
-                app.resetCachedInfo();
+            if (state.getAdjSeq() != mAdjSeq) {
+                state.setContainsCycle(false);
+                state.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
+                state.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
+                state.setSetCapability(PROCESS_CAPABILITY_NONE);
+                state.resetCachedInfo();
             }
         }
         for (int i = numProc - 1; i >= 0; i--) {
             ProcessRecord app = activeProcesses.get(i);
-            if (!app.killedByAm && app.thread != null) {
-                app.procStateChanged = false;
-                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, topApp, fullUpdate, now, false,
+            final ProcessStateRecord state = app.mState;
+            if (!app.isKilledByAm() && app.getThread() != null) {
+                state.setProcStateChanged(false);
+                computeOomAdjLSP(app, ProcessList.UNKNOWN_ADJ, topApp, fullUpdate, now, false,
                         computeClients); // It won't enter cycle if not computing clients.
                 // if any app encountered a cycle, we need to perform an additional loop later
-                retryCycles |= app.containsCycle;
+                retryCycles |= state.containsCycle();
                 // Keep the completedAdjSeq to up to date.
-                app.completedAdjSeq = mAdjSeq;
+                state.setCompletedAdjSeq(mAdjSeq);
             }
         }
 
         if (mCacheOomRanker.useOomReranking()) {
-            mCacheOomRanker.reRankLruCachedApps(mProcessList);
+            mCacheOomRanker.reRankLruCachedAppsLSP(mProcessList.getLruProcessesLSP(),
+                    mProcessList.getLruProcessServiceStartLOSP());
         }
-        assignCachedAdjIfNecessary(mProcessList.mLruProcesses);
+        assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
 
         if (computeClients) { // There won't be cycles if we didn't compute clients above.
             // Cycle strategy:
@@ -794,16 +828,18 @@
 
                 for (int i = 0; i < numProc; i++) {
                     ProcessRecord app = activeProcesses.get(i);
-                    if (!app.killedByAm && app.thread != null && app.containsCycle) {
-                        app.adjSeq--;
-                        app.completedAdjSeq--;
+                    final ProcessStateRecord state = app.mState;
+                    if (!app.isKilledByAm() && app.getThread() != null && state.containsCycle()) {
+                        state.decAdjSeq();
+                        state.decCompletedAdjSeq();
                     }
                 }
 
                 for (int i = 0; i < numProc; i++) {
                     ProcessRecord app = activeProcesses.get(i);
-                    if (!app.killedByAm && app.thread != null && app.containsCycle) {
-                        if (computeOomAdjLocked(app, app.getCurRawAdj(), topApp, true, now,
+                    final ProcessStateRecord state = app.mState;
+                    if (!app.isKilledByAm() && app.getThread() != null && state.containsCycle()) {
+                        if (computeOomAdjLSP(app, state.getCurRawAdj(), topApp, true, now,
                                 true, true)) {
                             retryCycles = true;
                         }
@@ -815,7 +851,7 @@
         mNumNonCachedProcs = 0;
         mNumCachedHiddenProcs = 0;
 
-        boolean allChanged = updateAndTrimProcessLocked(now, nowElapsed, oldTime, activeUids);
+        boolean allChanged = updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids);
         mNumServiceProcs = mNewNumServiceProcs;
 
         if (mService.mAlwaysFinishActivities) {
@@ -825,11 +861,11 @@
         }
 
         if (allChanged) {
-            mService.mAppProfiler.requestPssAllProcsLocked(now, false,
+            mService.mAppProfiler.requestPssAllProcsLPr(now, false,
                     mService.mProcessStats.isMemFactorLowered());
         }
 
-        updateUidsLocked(activeUids, nowElapsed);
+        updateUidsLSP(activeUids, nowElapsed);
 
         synchronized (mService.mProcessStats.mLock) {
             if (mService.mProcessStats.shouldWriteNowLocked(now)) {
@@ -856,6 +892,7 @@
         }
     }
 
+    @GuardedBy({"mService", "mProcLock"})
     private void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) {
         final int numLru = lruList.size();
 
@@ -899,23 +936,27 @@
 
         for (int i = numLru - 1; i >= 0; i--) {
             ProcessRecord app = lruList.get(i);
+            final ProcessStateRecord state = app.mState;
             // If we haven't yet assigned the final cached adj
             // to the process, do that now.
-            if (!app.killedByAm && app.thread != null && app.curAdj
+            if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
                     >= ProcessList.UNKNOWN_ADJ) {
-                switch (app.getCurProcState()) {
+                final ProcessServiceRecord psr = app.mServices;
+                switch (state.getCurProcState()) {
                     case PROCESS_STATE_CACHED_ACTIVITY:
                     case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                     case ActivityManager.PROCESS_STATE_CACHED_RECENT:
                         // Figure out the next cached level, taking into account groups.
                         boolean inGroup = false;
-                        if (app.connectionGroup != 0) {
+                        final int connectionGroup = psr.getConnectionGroup();
+                        if (connectionGroup != 0) {
+                            final int connectionImportance = psr.getConnectionImportance();
                             if (lastCachedGroupUid == app.uid
-                                    && lastCachedGroup == app.connectionGroup) {
+                                    && lastCachedGroup == connectionGroup) {
                                 // This is in the same group as the last process, just tweak
                                 // adjustment by importance.
-                                if (app.connectionImportance > lastCachedGroupImportance) {
-                                    lastCachedGroupImportance = app.connectionImportance;
+                                if (connectionImportance > lastCachedGroupImportance) {
+                                    lastCachedGroupImportance = connectionImportance;
                                     if (curCachedAdj < nextCachedAdj
                                             && curCachedAdj < ProcessList.CACHED_APP_MAX_ADJ) {
                                         curCachedImpAdj++;
@@ -924,8 +965,8 @@
                                 inGroup = true;
                             } else {
                                 lastCachedGroupUid = app.uid;
-                                lastCachedGroup = app.connectionGroup;
-                                lastCachedGroupImportance = app.connectionImportance;
+                                lastCachedGroup = connectionGroup;
+                                lastCachedGroupImportance = connectionImportance;
                             }
                         }
                         if (!inGroup && curCachedAdj != nextCachedAdj) {
@@ -943,11 +984,12 @@
                         // This process is a cached process holding activities...
                         // assign it the next cached value for that type, and then
                         // step that cached level.
-                        app.setCurRawAdj(curCachedAdj + curCachedImpAdj);
-                        app.curAdj = app.modifyRawOomAdj(curCachedAdj + curCachedImpAdj);
+                        state.setCurRawAdj(curCachedAdj + curCachedImpAdj);
+                        state.setCurAdj(psr.modifyRawOomAdj(curCachedAdj + curCachedImpAdj));
                         if (DEBUG_LRU) {
                             Slog.d(TAG_LRU, "Assigning activity LRU #" + i
-                                    + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
+                                    + " adj: " + state.getCurAdj()
+                                    + " (curCachedAdj=" + curCachedAdj
                                     + " curCachedImpAdj=" + curCachedImpAdj + ")");
                         }
                         break;
@@ -969,11 +1011,11 @@
                         // long-running services that have dropped down to the
                         // cached level will be treated as empty (since their process
                         // state is still as a service), which is what we want.
-                        app.setCurRawAdj(curEmptyAdj);
-                        app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
+                        state.setCurRawAdj(curEmptyAdj);
+                        state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj));
                         if (DEBUG_LRU) {
                             Slog.d(TAG_LRU, "Assigning empty LRU #" + i
-                                    + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
+                                    + " adj: " + state.getCurAdj() + " (curEmptyAdj=" + curEmptyAdj
                                     + ")");
                         }
                         break;
@@ -982,9 +1024,10 @@
         }
     }
 
-    private boolean updateAndTrimProcessLocked(final long now, final long nowElapsed,
+    @GuardedBy({"mService", "mProcLock"})
+    private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed,
             final long oldTime, final ActiveUids activeUids) {
-        ArrayList<ProcessRecord> lruList = mProcessList.mLruProcesses;
+        ArrayList<ProcessRecord> lruList = mProcessList.getLruProcessesLOSP();
         final int numLru = lruList.size();
 
         final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
@@ -999,34 +1042,37 @@
 
         for (int i = numLru - 1; i >= 0; i--) {
             ProcessRecord app = lruList.get(i);
-            if (!app.killedByAm && app.thread != null) {
+            final ProcessStateRecord state = app.mState;
+            if (!app.isKilledByAm() && app.getThread() != null) {
                 // We don't need to apply the update for the process which didn't get computed
-                if (app.completedAdjSeq == mAdjSeq) {
-                    applyOomAdjLocked(app, true, now, nowElapsed);
+                if (state.getCompletedAdjSeq() == mAdjSeq) {
+                    applyOomAdjLSP(app, true, now, nowElapsed);
                 }
 
+                final ProcessServiceRecord psr = app.mServices;
                 // Count the number of process types.
-                switch (app.getCurProcState()) {
+                switch (state.getCurProcState()) {
                     case PROCESS_STATE_CACHED_ACTIVITY:
                     case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                         mNumCachedHiddenProcs++;
                         numCached++;
-                        if (app.connectionGroup != 0) {
+                        final int connectionGroup = psr.getConnectionGroup();
+                        if (connectionGroup != 0) {
                             if (lastCachedGroupUid == app.info.uid
-                                    && lastCachedGroup == app.connectionGroup) {
+                                    && lastCachedGroup == connectionGroup) {
                                 // If this process is the next in the same group, we don't
                                 // want it to count against our limit of the number of cached
                                 // processes, so bump up the group count to account for it.
                                 numCachedExtraGroup++;
                             } else {
                                 lastCachedGroupUid = app.info.uid;
-                                lastCachedGroup = app.connectionGroup;
+                                lastCachedGroup = connectionGroup;
                             }
                         } else {
                             lastCachedGroupUid = lastCachedGroup = 0;
                         }
                         if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
-                            app.kill("cached #" + numCached,
+                            app.killLocked("cached #" + numCached,
                                     ApplicationExitInfo.REASON_OTHER,
                                     ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED,
                                     true);
@@ -1034,17 +1080,16 @@
                         break;
                     case PROCESS_STATE_CACHED_EMPTY:
                         if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
-                                && app.lastActivityTime < oldTime) {
-                            app.kill("empty for "
-                                    + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
-                                    / 1000) + "s",
+                                && app.getLastActivityTime() < oldTime) {
+                            app.killLocked("empty for " + ((oldTime + ProcessList.MAX_EMPTY_TIME
+                                    - app.getLastActivityTime()) / 1000) + "s",
                                     ApplicationExitInfo.REASON_OTHER,
                                     ApplicationExitInfo.SUBREASON_TRIM_EMPTY,
                                     true);
                         } else {
                             numEmpty++;
                             if (numEmpty > emptyProcessLimit) {
-                                app.kill("empty #" + numEmpty,
+                                app.killLocked("empty #" + numEmpty,
                                         ApplicationExitInfo.REASON_OTHER,
                                         ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY,
                                         true);
@@ -1056,8 +1101,8 @@
                         break;
                 }
 
-                if (app.isolated && app.numberOfRunningServices() <= 0
-                        && app.isolatedEntryPoint == null) {
+                if (app.isolated && psr.numberOfRunningServices() <= 0
+                        && app.getIsolatedEntryPoint() == null) {
                     // If this is an isolated process, there are no services
                     // running in it, and it's not a special process with a
                     // custom entry point, then the process is no longer
@@ -1065,40 +1110,56 @@
                     // definition not re-use the same process again, and it is
                     // good to avoid having whatever code was running in them
                     // left sitting around after no longer needed.
-                    app.kill("isolated not needed", ApplicationExitInfo.REASON_OTHER,
+                    app.killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
                             ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true);
                 } else {
                     // Keeping this process, update its uid.
-                    updateAppUidRecLocked(app);
+                    updateAppUidRecLSP(app);
                 }
 
-                if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
-                        && !app.killedByAm) {
+                if (state.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
+                        && !app.isKilledByAm()) {
                     numTrimming++;
                 }
             }
         }
 
-        mProcessList.incrementProcStateSeqAndNotifyAppsLocked(activeUids);
+        mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(activeUids);
 
-        return mService.mAppProfiler.updateLowMemStateLocked(numCached, numEmpty, numTrimming);
+        return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming);
     }
 
-    private void updateAppUidRecLocked(ProcessRecord app) {
-        final UidRecord uidRec = app.uidRecord;
-        if (uidRec != null) {
-            uidRec.ephemeral = app.info.isInstantApp();
-            if (uidRec.getCurProcState() > app.getCurProcState()) {
-                uidRec.setCurProcState(app.getCurProcState());
+    @GuardedBy({"mService", "mProcLock"})
+    private void updateAppUidRecIfNecessaryLSP(final ProcessRecord app) {
+        if (!app.isKilledByAm() && app.getThread() != null) {
+            if (app.isolated && app.mServices.numberOfRunningServices() <= 0
+                    && app.getIsolatedEntryPoint() == null) {
+                // No op.
+            } else {
+                // Keeping this process, update its uid.
+                updateAppUidRecLSP(app);
             }
-            if (app.hasForegroundServices()) {
-                uidRec.foregroundServices = true;
-            }
-            uidRec.curCapability |= app.curCapability;
         }
     }
 
-    private void updateUidsLocked(ActiveUids activeUids, final long nowElapsed) {
+    @GuardedBy({"mService", "mProcLock"})
+    private void updateAppUidRecLSP(ProcessRecord app) {
+        final UidRecord uidRec = app.getUidRecord();
+        if (uidRec != null) {
+            final ProcessStateRecord state = app.mState;
+            uidRec.setEphemeral(app.info.isInstantApp());
+            if (uidRec.getCurProcState() > state.getCurProcState()) {
+                uidRec.setCurProcState(state.getCurProcState());
+            }
+            if (app.mServices.hasForegroundServices()) {
+                uidRec.setForegroundServices(true);
+            }
+            uidRec.setCurCapability(uidRec.getCurCapability() | state.getCurCapability());
+        }
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    private void updateUidsLSP(ActiveUids activeUids, final long nowElapsed) {
         ArrayList<UidRecord> becameIdle = mTmpBecameIdle;
         becameIdle.clear();
 
@@ -1110,22 +1171,22 @@
             final UidRecord uidRec = activeUids.valueAt(i);
             int uidChange = UidRecord.CHANGE_PROCSTATE;
             if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
-                    && (uidRec.setProcState != uidRec.getCurProcState()
-                    || uidRec.setCapability != uidRec.curCapability
-                    || uidRec.mSetAllowlist != uidRec.mCurAllowlist)) {
+                    && (uidRec.getSetProcState() != uidRec.getCurProcState()
+                    || uidRec.getSetCapability() != uidRec.getCurCapability()
+                    || uidRec.isSetAllowListed() != uidRec.isCurAllowListed())) {
                 if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec
-                        + ": proc state from " + uidRec.setProcState + " to "
+                        + ": proc state from " + uidRec.getSetProcState() + " to "
                         + uidRec.getCurProcState() + ", capability from "
-                        + uidRec.setCapability + " to " + uidRec.curCapability
-                        + ", allowlist from " + uidRec.mSetAllowlist
-                        + " to " + uidRec.mCurAllowlist);
+                        + uidRec.getSetCapability() + " to " + uidRec.getCurCapability()
+                        + ", allowlist from " + uidRec.isSetAllowListed()
+                        + " to " + uidRec.isCurAllowListed());
                 if (ActivityManager.isProcStateBackground(uidRec.getCurProcState())
-                        && !uidRec.mCurAllowlist) {
+                        && !uidRec.isCurAllowListed()) {
                     // UID is now in the background (and not on the temp allowlist).  Was it
                     // previously in the foreground (or on the temp allowlist)?
-                    if (!ActivityManager.isProcStateBackground(uidRec.setProcState)
-                            || uidRec.mSetAllowlist) {
-                        uidRec.lastBackgroundTime = nowElapsed;
+                    if (!ActivityManager.isProcStateBackground(uidRec.getSetProcState())
+                            || uidRec.isSetAllowListed()) {
+                        uidRec.setLastBackgroundTime(nowElapsed);
                         if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
                             // Note: the background settle time is in elapsed realtime, while
                             // the handler time base is uptime.  All this means is that we may
@@ -1135,38 +1196,40 @@
                                     mConstants.BACKGROUND_SETTLE_TIME);
                         }
                     }
-                    if (uidRec.idle && !uidRec.setIdle) {
+                    if (uidRec.isIdle() && !uidRec.isSetIdle()) {
                         uidChange = UidRecord.CHANGE_IDLE;
                         becameIdle.add(uidRec);
                     }
                 } else {
-                    if (uidRec.idle) {
+                    if (uidRec.isIdle()) {
                         uidChange = UidRecord.CHANGE_ACTIVE;
-                        EventLogTags.writeAmUidActive(uidRec.uid);
-                        uidRec.idle = false;
+                        EventLogTags.writeAmUidActive(uidRec.getUid());
+                        uidRec.setIdle(false);
                     }
-                    uidRec.lastBackgroundTime = 0;
+                    uidRec.setLastBackgroundTime(0);
                 }
-                final boolean wasCached = uidRec.setProcState
+                final boolean wasCached = uidRec.getSetProcState()
                         > ActivityManager.PROCESS_STATE_RECEIVER;
                 final boolean isCached = uidRec.getCurProcState()
                         > ActivityManager.PROCESS_STATE_RECEIVER;
-                if (wasCached != isCached || uidRec.setProcState == PROCESS_STATE_NONEXISTENT) {
+                if (wasCached != isCached
+                        || uidRec.getSetProcState() == PROCESS_STATE_NONEXISTENT) {
                     uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
                 }
-                uidRec.setProcState = uidRec.getCurProcState();
-                uidRec.setCapability = uidRec.curCapability;
-                uidRec.mSetAllowlist = uidRec.mCurAllowlist;
-                uidRec.setIdle = uidRec.idle;
-                mService.mAtmInternal.onUidProcStateChanged(uidRec.uid, uidRec.setProcState);
+                uidRec.setSetProcState(uidRec.getCurProcState());
+                uidRec.setSetCapability(uidRec.getCurCapability());
+                uidRec.setSetAllowListed(uidRec.isCurAllowListed());
+                uidRec.setSetIdle(uidRec.isIdle());
+                mService.mAtmInternal.onUidProcStateChanged(
+                        uidRec.getUid(), uidRec.getSetProcState());
                 mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
-                mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState(),
-                        uidRec.curCapability);
-                if (uidRec.foregroundServices) {
+                mService.noteUidProcessState(uidRec.getUid(), uidRec.getCurProcState(),
+                        uidRec.getCurCapability());
+                if (uidRec.hasForegroundServices()) {
                     mService.mServices.foregroundServiceProcStateChangedLocked(uidRec);
                 }
             }
-            mService.mInternal.deletePendingTopUid(uidRec.uid);
+            mService.mInternal.deletePendingTopUid(uidRec.getUid());
         }
         if (mLocalPowerManager != null) {
             mLocalPowerManager.finishUidChanges();
@@ -1177,7 +1240,7 @@
             // If we have any new uids that became idle this time, we need to make sure
             // they aren't left with running services.
             for (int i = size - 1; i >= 0; i--) {
-                mService.mServices.stopInBackgroundLocked(becameIdle.get(i).uid);
+                mService.mServices.stopInBackgroundLocked(becameIdle.get(i).getUid());
             }
         }
     }
@@ -1185,7 +1248,7 @@
     private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
             new ComputeOomAdjWindowCallback();
 
-    /** These methods are called inline during computeOomAdjLocked(), on the same thread */
+    /** These methods are called inline during computeOomAdjLSP(), on the same thread */
     final class ComputeOomAdjWindowCallback
             implements WindowProcessController.ComputeOomAdjCallback {
 
@@ -1197,6 +1260,7 @@
         int appUid;
         int logUid;
         int processStateCurTop;
+        ProcessStateRecord mState;
 
         void initialize(ProcessRecord app, int adj, boolean foregroundActivities,
                 int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) {
@@ -1208,6 +1272,7 @@
             this.appUid = appUid;
             this.logUid = logUid;
             this.processStateCurTop = processStateCurTop;
+            this.mState = app.mState;
         }
 
         @Override
@@ -1215,14 +1280,14 @@
             // App has a visible activity; only upgrade adjustment.
             if (adj > ProcessList.VISIBLE_APP_ADJ) {
                 adj = ProcessList.VISIBLE_APP_ADJ;
-                app.adjType = "vis-activity";
+                mState.setAdjType("vis-activity");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to vis-activity: " + app);
                 }
             }
             if (procState > processStateCurTop) {
                 procState = processStateCurTop;
-                app.adjType = "vis-activity";
+                mState.setAdjType("vis-activity");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ,
                             "Raise procstate to vis-activity (top): " + app);
@@ -1231,8 +1296,8 @@
             if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
                 schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
             }
-            app.setCached(false);
-            app.empty = false;
+            mState.setCached(false);
+            mState.setEmpty(false);
             foregroundActivities = true;
         }
 
@@ -1240,14 +1305,14 @@
         public void onPausedActivity() {
             if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                app.adjType = "pause-activity";
+                mState.setAdjType("pause-activity");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to pause-activity: "  + app);
                 }
             }
             if (procState > processStateCurTop) {
                 procState = processStateCurTop;
-                app.adjType = "pause-activity";
+                mState.setAdjType("pause-activity");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ,
                             "Raise procstate to pause-activity (top): "  + app);
@@ -1256,8 +1321,8 @@
             if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
                 schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
             }
-            app.setCached(false);
-            app.empty = false;
+            mState.setCached(false);
+            mState.setEmpty(false);
             foregroundActivities = true;
         }
 
@@ -1265,7 +1330,7 @@
         public void onStoppingActivity(boolean finishing) {
             if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                app.adjType = "stop-activity";
+                mState.setAdjType("stop-activity");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ,
                             "Raise adj to stop-activity: "  + app);
@@ -1281,15 +1346,15 @@
             if (!finishing) {
                 if (procState > PROCESS_STATE_LAST_ACTIVITY) {
                     procState = PROCESS_STATE_LAST_ACTIVITY;
-                    app.adjType = "stop-activity";
+                    mState.setAdjType("stop-activity");
                     if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                         reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                 "Raise procstate to stop-activity: " + app);
                     }
                 }
             }
-            app.setCached(false);
-            app.empty = false;
+            mState.setCached(false);
+            mState.setEmpty(false);
             foregroundActivities = true;
         }
 
@@ -1297,7 +1362,7 @@
         public void onOtherActivity() {
             if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
                 procState = PROCESS_STATE_CACHED_ACTIVITY;
-                app.adjType = "cch-act";
+                mState.setAdjType("cch-act");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ,
                             "Raise procstate to cached activity: " + app);
@@ -1306,99 +1371,102 @@
         }
     }
 
-    private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
+    @GuardedBy({"mService", "mProcLock"})
+    private boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj,
             ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
             boolean computeClients) {
-        if (mAdjSeq == app.adjSeq) {
-            if (app.adjSeq == app.completedAdjSeq) {
+        final ProcessStateRecord state = app.mState;
+        if (mAdjSeq == state.getAdjSeq()) {
+            if (state.getAdjSeq() == state.getCompletedAdjSeq()) {
                 // This adjustment has already been computed successfully.
                 return false;
             } else {
                 // The process is being computed, so there is a cycle. We cannot
                 // rely on this process's state.
-                app.containsCycle = true;
+                state.setContainsCycle(true);
 
                 return false;
             }
         }
 
-        if (app.thread == null) {
-            app.adjSeq = mAdjSeq;
-            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
-            app.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
-            app.curAdj = ProcessList.CACHED_APP_MAX_ADJ;
-            app.setCurRawAdj(ProcessList.CACHED_APP_MAX_ADJ);
-            app.completedAdjSeq = app.adjSeq;
-            app.curCapability = PROCESS_CAPABILITY_NONE;
+        if (app.getThread() == null) {
+            state.setAdjSeq(mAdjSeq);
+            state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
+            state.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
+            state.setCurAdj(ProcessList.CACHED_APP_MAX_ADJ);
+            state.setCurRawAdj(ProcessList.CACHED_APP_MAX_ADJ);
+            state.setCompletedAdjSeq(state.getAdjSeq());
+            state.setCurCapability(PROCESS_CAPABILITY_NONE);
             return false;
         }
 
-        app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
-        app.adjSource = null;
-        app.adjTarget = null;
-        app.empty = false;
-        app.setCached(false);
-        app.shouldNotFreeze = false;
-
-        app.resetAllowStartFgs();
+        state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN);
+        state.setAdjSource(null);
+        state.setAdjTarget(null);
+        state.setEmpty(false);
+        state.setCached(false);
+        state.setAllowStartFgsState(PROCESS_STATE_NONEXISTENT);
+        state.resetAllowStartFgs();
+        app.mOptRecord.setShouldNotFreeze(false);
 
         final int appUid = app.info.uid;
         final int logUid = mService.mCurOomAdjUid;
 
-        int prevAppAdj = app.curAdj;
-        int prevProcState = app.getCurProcState();
-        int prevCapability = app.curCapability;
+        int prevAppAdj = state.getCurAdj();
+        int prevProcState = state.getCurProcState();
+        int prevCapability = state.getCurCapability();
+        final ProcessServiceRecord psr = app.mServices;
 
-        if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
+        if (state.getMaxAdj() <= ProcessList.FOREGROUND_APP_ADJ) {
             // The max adjustment doesn't allow this app to be anything
             // below foreground, so it is not worth doing work for it.
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app);
             }
-            app.adjType = "fixed";
-            app.adjSeq = mAdjSeq;
-            app.setCurRawAdj(app.maxAdj);
-            app.setHasForegroundActivities(false);
-            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
-            app.curCapability = PROCESS_CAPABILITY_ALL;
-            app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
+            state.setAdjType("fixed");
+            state.setAdjSeq(mAdjSeq);
+            state.setCurRawAdj(state.getMaxAdj());
+            state.setHasForegroundActivities(false);
+            state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+            state.setCurCapability(PROCESS_CAPABILITY_ALL);
+            state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
             // System processes can do UI, and when they do we want to have
             // them trim their memory after the user leaves the UI.  To
             // facilitate this, here we need to determine whether or not it
             // is currently showing UI.
-            app.systemNoUi = true;
+            state.setSystemNoUi(true);
             if (app == topApp) {
-                app.systemNoUi = false;
-                app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
-                app.adjType = "pers-top-activity";
-            } else if (app.hasTopUi()) {
+                state.setSystemNoUi(false);
+                state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+                state.setAdjType("pers-top-activity");
+            } else if (state.hasTopUi()) {
                 // sched group/proc state adjustment is below
-                app.systemNoUi = false;
-                app.adjType = "pers-top-ui";
-            } else if (app.getCachedHasVisibleActivities()) {
-                app.systemNoUi = false;
+                state.setSystemNoUi(false);
+                state.setAdjType("pers-top-ui");
+            } else if (state.getCachedHasVisibleActivities()) {
+                state.setSystemNoUi(false);
             }
-            if (!app.systemNoUi) {
+            if (!state.isSystemNoUi()) {
                 if (mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE) {
                     // screen on, promote UI
-                    app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
-                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+                    state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+                    state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
                 } else {
                     // screen off, restrict UI scheduling
-                    app.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
+                    state.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+                    state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
                 }
             }
-            app.setCurRawProcState(app.getCurProcState());
-            app.curAdj = app.maxAdj;
-            app.completedAdjSeq = app.adjSeq;
-            app.bumpAllowStartFgsState(app.getCurProcState());
-            app.setAllowStartFgs();
+            state.setCurRawProcState(state.getCurProcState());
+            state.setCurAdj(state.getMaxAdj());
+            state.setCompletedAdjSeq(state.getAdjSeq());
+            state.bumpAllowStartFgsState(state.getCurProcState());
+            state.setAllowStartFgs();
             // if curAdj is less than prevAppAdj, then this process was promoted
-            return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
+            return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState;
         }
 
-        app.systemNoUi = false;
+        state.setSystemNoUi(false);
 
         final int PROCESS_STATE_CUR_TOP = mService.mAtmInternal.getTopProcessState();
 
@@ -1415,17 +1483,17 @@
             // The last app on the list is the foreground app.
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
-            app.adjType = "top-activity";
+            state.setAdjType("top-activity");
             foregroundActivities = true;
             procState = PROCESS_STATE_CUR_TOP;
-            app.bumpAllowStartFgsState(PROCESS_STATE_TOP);
+            state.bumpAllowStartFgsState(PROCESS_STATE_TOP);
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app);
             }
-        } else if (app.runningRemoteAnimation) {
+        } else if (state.isRunningRemoteAnimation()) {
             adj = ProcessList.VISIBLE_APP_ADJ;
             schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
-            app.adjType = "running-remote-anim";
+            state.setAdjType("running-remote-anim");
             procState = PROCESS_STATE_CUR_TOP;
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app);
@@ -1434,12 +1502,12 @@
             // Don't want to kill running instrumentation.
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            app.adjType = "instrumentation";
+            state.setAdjType("instrumentation");
             procState = PROCESS_STATE_FOREGROUND_SERVICE;
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app);
             }
-        } else if (app.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) {
+        } else if (state.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) {
             // An app that is currently receiving a broadcast also
             // counts as being in the foreground for OOM killer purposes.
             // It's placed in a sched group based on the nature of the
@@ -1447,27 +1515,26 @@
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = (mTmpBroadcastQueue.contains(mService.mFgBroadcastQueue))
                     ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
-            app.adjType = "broadcast";
+            state.setAdjType("broadcast");
             procState = ActivityManager.PROCESS_STATE_RECEIVER;
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app);
             }
-        } else if (app.executingServices.size() > 0) {
+        } else if (psr.numberOfExecutingServices() > 0) {
             // An app that is currently executing a service callback also
             // counts as being in the foreground.
             adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = app.execServicesFg ?
-                    ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
-            app.adjType = "exec-service";
+            schedGroup = psr.shouldExecServicesFg()
+                    ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
+            state.setAdjType("exec-service");
             procState = PROCESS_STATE_SERVICE;
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app);
             }
-            //Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);
         } else if (app == topApp) {
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-            app.adjType = "top-sleeping";
+            state.setAdjType("top-sleeping");
             foregroundActivities = true;
             procState = PROCESS_STATE_CUR_TOP;
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -1480,10 +1547,10 @@
             // value that the caller wants us to.
             adj = cachedAdj;
             procState = PROCESS_STATE_CACHED_EMPTY;
-            if (!app.containsCycle) {
-                app.setCached(true);
-                app.empty = true;
-                app.adjType = "cch-empty";
+            if (!state.containsCycle()) {
+                state.setCached(true);
+                state.setEmpty(true);
+                state.setAdjType("cch-empty");
             }
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making empty: " + app);
@@ -1491,20 +1558,20 @@
         }
 
         // Examine all activities if not already foreground.
-        if (!foregroundActivities && app.getCachedHasActivities()) {
-            app.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback,
+        if (!foregroundActivities && state.getCachedHasActivities()) {
+            state.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback,
                     adj, foregroundActivities, procState, schedGroup, appUid, logUid,
                     PROCESS_STATE_CUR_TOP);
 
-            adj = app.mCachedAdj;
-            foregroundActivities = app.mCachedForegroundActivities;
-            procState = app.mCachedProcState;
-            schedGroup = app.mCachedSchedGroup;
+            adj = state.getCachedAdj();
+            foregroundActivities = state.getCachedForegroundActivities();
+            procState = state.getCachedProcState();
+            schedGroup = state.getCachedSchedGroup();
         }
 
-        if (procState > PROCESS_STATE_CACHED_RECENT && app.getCachedHasRecentTasks()) {
+        if (procState > PROCESS_STATE_CACHED_RECENT && state.getCachedHasRecentTasks()) {
             procState = PROCESS_STATE_CACHED_RECENT;
-            app.adjType = "cch-rec";
+            state.setAdjType("cch-rec");
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app);
             }
@@ -1512,24 +1579,24 @@
 
         if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
                 || procState > PROCESS_STATE_FOREGROUND_SERVICE) {
-            if (app.hasForegroundServices()) {
+            if (psr.hasForegroundServices()) {
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                 procState = PROCESS_STATE_FOREGROUND_SERVICE;
-                app.bumpAllowStartFgsState(PROCESS_STATE_FOREGROUND_SERVICE);
-                app.adjType = "fg-service";
-                app.setCached(false);
+                state.bumpAllowStartFgsState(PROCESS_STATE_FOREGROUND_SERVICE);
+                state.setAdjType("fg-service");
+                state.setCached(false);
                 schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + app.adjType + ": "
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + state.getAdjType() + ": "
                             + app + " ");
                 }
-            } else if (app.hasOverlayUi()) {
+            } else if (state.hasOverlayUi()) {
                 // The process is display an overlay UI.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                 procState = PROCESS_STATE_IMPORTANT_FOREGROUND;
-                app.setCached(false);
-                app.adjType = "has-overlay-ui";
+                state.setCached(false);
+                state.setAdjType("has-overlay-ui");
                 schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to overlay ui: " + app);
@@ -1540,11 +1607,11 @@
         // If the app was recently in the foreground and moved to a foreground service status,
         // allow it to get a higher rank in memory for some time, compared to other foreground
         // services so that it can finish performing any persistence/processing of in-memory state.
-        if (app.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
-                && (app.lastTopTime + mConstants.TOP_TO_FGS_GRACE_DURATION > now
-                || app.setProcState <= PROCESS_STATE_TOP)) {
+        if (psr.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
+                && (state.getLastTopTime() + mConstants.TOP_TO_FGS_GRACE_DURATION > now
+                || state.getSetProcState() <= PROCESS_STATE_TOP)) {
             adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
-            app.adjType = "fg-service-act";
+            state.setAdjType("fg-service-act");
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app);
             }
@@ -1552,15 +1619,15 @@
 
         if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
                 || procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {
-            if (app.forcingToImportant != null) {
+            if (state.getForcingToImportant() != null) {
                 // This is currently used for toasts...  they are not interactive, and
                 // we don't want them to cause the app to become fully foreground (and
                 // thus out of background check), so we yes the best background level we can.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                 procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
-                app.setCached(false);
-                app.adjType = "force-imp";
-                app.adjSource = app.forcingToImportant;
+                state.setCached(false);
+                state.setAdjType("force-imp");
+                state.setAdjSource(state.getForcingToImportant());
                 schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to force imp: " + app);
@@ -1568,63 +1635,63 @@
             }
         }
 
-        if (app.getCachedIsHeavyWeight()) {
+        if (state.getCachedIsHeavyWeight()) {
             if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
                 // We don't want to kill the current heavy-weight process.
                 adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
                 schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.setCached(false);
-                app.adjType = "heavy";
+                state.setCached(false);
+                state.setAdjType("heavy");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app);
                 }
             }
             if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
                 procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
-                app.adjType = "heavy";
+                state.setAdjType("heavy");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to heavy: " + app);
                 }
             }
         }
 
-        if (app.getCachedIsHomeProcess()) {
+        if (state.getCachedIsHomeProcess()) {
             if (adj > ProcessList.HOME_APP_ADJ) {
                 // This process is hosting what we currently consider to be the
                 // home app, so we don't want to let it go into the background.
                 adj = ProcessList.HOME_APP_ADJ;
                 schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.setCached(false);
-                app.adjType = "home";
+                state.setCached(false);
+                state.setAdjType("home");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app);
                 }
             }
             if (procState > ActivityManager.PROCESS_STATE_HOME) {
                 procState = ActivityManager.PROCESS_STATE_HOME;
-                app.adjType = "home";
+                state.setAdjType("home");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app);
                 }
             }
         }
 
-        if (app.getCachedIsPreviousProcess() && app.getCachedHasActivities()) {
+        if (state.getCachedIsPreviousProcess() && state.getCachedHasActivities()) {
             if (adj > ProcessList.PREVIOUS_APP_ADJ) {
                 // This was the previous process that showed UI to the user.
                 // We want to try to keep it around more aggressively, to give
                 // a good experience around switching between two apps.
                 adj = ProcessList.PREVIOUS_APP_ADJ;
                 schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.setCached(false);
-                app.adjType = "previous";
+                state.setCached(false);
+                state.setAdjType("previous");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
                 }
             }
             if (procState > PROCESS_STATE_LAST_ACTIVITY) {
                 procState = PROCESS_STATE_LAST_ACTIVITY;
-                app.adjType = "previous";
+                state.setAdjType("previous");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
                 }
@@ -1632,7 +1699,7 @@
         }
 
         if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
-                + " reason=" + app.adjType);
+                + " reason=" + state.getAdjType());
 
         // By default, we use the computed adjustment.  It may be changed if
         // there are applications dependent on our services or providers, but
@@ -1640,15 +1707,15 @@
         // infinite recursion. If we're re-evaluating due to cycles, use the previously computed
         // values.
         if (cycleReEval) {
-            procState = Math.min(procState, app.getCurRawProcState());
-            adj = Math.min(adj, app.getCurRawAdj());
-            schedGroup = Math.max(schedGroup, app.getCurrentSchedulingGroup());
+            procState = Math.min(procState, state.getCurRawProcState());
+            adj = Math.min(adj, state.getCurRawAdj());
+            schedGroup = Math.max(schedGroup, state.getCurrentSchedulingGroup());
         }
-        app.setCurRawAdj(adj);
-        app.setCurRawProcState(procState);
+        state.setCurRawAdj(adj);
+        state.setCurRawProcState(procState);
 
-        app.hasStartedServices = false;
-        app.adjSeq = mAdjSeq;
+        state.setHasStartedServices(false);
+        state.setAdjSeq(mAdjSeq);
 
         final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId);
         if (backupTarget != null && app == backupTarget.app) {
@@ -1659,15 +1726,15 @@
                 if (procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {
                     procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
                 }
-                app.adjType = "backup";
+                state.setAdjType("backup");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app);
                 }
-                app.setCached(false);
+                state.setCached(false);
             }
             if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
                 procState = ActivityManager.PROCESS_STATE_BACKUP;
-                app.adjType = "backup";
+                state.setAdjType("backup");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to backup: " + app);
                 }
@@ -1675,29 +1742,29 @@
         }
 
         int capabilityFromFGS = 0; // capability from foreground service.
-        for (int is = app.numberOfRunningServices() - 1;
+        for (int is = psr.numberOfRunningServices() - 1;
                 is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                         || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                         || procState > PROCESS_STATE_TOP);
                 is--) {
-            ServiceRecord s = app.getRunningServiceAt(is);
+            ServiceRecord s = psr.getRunningServiceAt(is);
             if (s.startRequested) {
-                app.hasStartedServices = true;
+                state.setHasStartedServices(true);
                 if (procState > PROCESS_STATE_SERVICE) {
                     procState = PROCESS_STATE_SERVICE;
-                    app.adjType = "started-services";
+                    state.setAdjType("started-services");
                     if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                         reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                 "Raise procstate to started service: " + app);
                     }
                 }
-                if (!s.mKeepWarming && app.hasShownUi && !app.getCachedIsHomeProcess()) {
+                if (!s.mKeepWarming && state.hasShownUi() && !state.getCachedIsHomeProcess()) {
                     // If this process has shown some UI, let it immediately
                     // go to the LRU list because it may be pretty heavy with
                     // UI stuff.  We'll tag it with a label just to help
                     // debug and understand what is going on.
                     if (adj > ProcessList.SERVICE_ADJ) {
-                        app.adjType = "cch-started-ui-services";
+                        state.setAdjType("cch-started-ui-services");
                     }
                 } else {
                     if (s.mKeepWarming
@@ -1707,19 +1774,19 @@
                         // of the background processes.
                         if (adj > ProcessList.SERVICE_ADJ) {
                             adj = ProcessList.SERVICE_ADJ;
-                            app.adjType = "started-services";
+                            state.setAdjType("started-services");
                             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                                 reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                         "Raise adj to started service: " + app);
                             }
-                            app.setCached(false);
+                            state.setCached(false);
                         }
                     }
                     // If we have let the service slide into the background
                     // state, still have some text describing what it is doing
                     // even though the service no longer has an impact.
                     if (adj > ProcessList.SERVICE_ADJ) {
-                        app.adjType = "cch-started-services";
+                        state.setAdjType("cch-started-services");
                     }
                 }
             }
@@ -1774,29 +1841,31 @@
                     boolean trackedProcState = false;
 
                     ProcessRecord client = cr.binding.client;
+                    final ProcessStateRecord cstate = client.mState;
                     if (computeClients) {
-                        computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now,
+                        computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now,
                                 cycleReEval, true);
                     } else {
-                        client.setCurRawAdj(client.setAdj);
-                        client.setCurRawProcState(client.setProcState);
+                        cstate.setCurRawAdj(cstate.getSetAdj());
+                        cstate.setCurRawProcState(cstate.getSetProcState());
                     }
 
-                    int clientAdj = client.getCurRawAdj();
-                    int clientProcState = client.getCurRawProcState();
+                    int clientAdj = cstate.getCurRawAdj();
+                    int clientProcState = cstate.getCurRawProcState();
 
                     // pass client's mAllowStartFgs to the app if client is not persistent process.
-                    if (client.mAllowStartFgs && client.maxAdj >= ProcessList.FOREGROUND_APP_ADJ) {
-                        app.mAllowStartFgs = true;
+                    if (cstate.getAllowedStartFgs() != FGS_FEATURE_DENIED
+                            && cstate.getMaxAdj() >= ProcessList.FOREGROUND_APP_ADJ) {
+                        state.setAllowStartFgs(cstate.getAllowedStartFgs());
                     }
 
                     if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {
-                        if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                        if (shouldSkipDueToCycle(state, cstate, procState, adj, cycleReEval)) {
                             continue;
                         }
 
                         if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
-                            capability |= client.curCapability;
+                            capability |= cstate.getCurCapability();
                         }
 
                         if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
@@ -1809,7 +1878,7 @@
                         if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
                             // Not doing bind OOM management, so treat
                             // this guy more like a started service.
-                            if (app.hasShownUi && !app.getCachedIsHomeProcess()) {
+                            if (state.hasShownUi() && !state.getCachedIsHomeProcess()) {
                                 // If this process has shown some UI, let it immediately
                                 // go to the LRU list because it may be pretty heavy with
                                 // UI stuff.  We'll tag it with a label just to help
@@ -1817,7 +1886,7 @@
                                 if (adj > clientAdj) {
                                     adjType = "cch-bound-ui-services";
                                 }
-                                app.setCached(false);
+                                state.setCached(false);
                                 clientAdj = adj;
                                 clientProcState = procState;
                             } else {
@@ -1843,7 +1912,7 @@
                             // about letting this process get into the LRU
                             // list to be killed and restarted if needed for
                             // memory.
-                            if (app.hasShownUi && !app.getCachedIsHomeProcess()
+                            if (state.hasShownUi() && !state.getCachedIsHomeProcess()
                                     && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                                 if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
                                     adjType = "cch-bound-ui-services";
@@ -1880,12 +1949,12 @@
                                         newAdj = adj;
                                     }
                                 }
-                                if (!client.isCached()) {
-                                    app.setCached(false);
+                                if (!cstate.isCached()) {
+                                    state.setCached(false);
                                 }
                                 if (adj >  newAdj) {
                                     adj = newAdj;
-                                    app.setCurRawAdj(adj);
+                                    state.setCurRawAdj(adj);
                                     adjType = "service";
                                 }
                             }
@@ -1895,7 +1964,7 @@
                             // This will treat important bound services identically to
                             // the top app, which may behave differently than generic
                             // foreground work.
-                            final int curSchedGroup = client.getCurrentSchedulingGroup();
+                            final int curSchedGroup = cstate.getCurrentSchedulingGroup();
                             if (curSchedGroup > schedGroup) {
                                 if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
                                     schedGroup = curSchedGroup;
@@ -1910,7 +1979,7 @@
                                 // give them the best bound state after that.
                                 if (cr.hasFlag(Context.BIND_FOREGROUND_SERVICE)) {
                                     clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-                                    app.bumpAllowStartFgsState(
+                                    state.bumpAllowStartFgsState(
                                             PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
                                 } else if (mService.mWakefulness.get()
                                         == PowerManagerInternal.WAKEFULNESS_AWAKE
@@ -1925,7 +1994,7 @@
                                 // Go at most to BOUND_TOP, unless requested to elevate
                                 // to client's state.
                                 clientProcState = PROCESS_STATE_BOUND_TOP;
-                                app.bumpAllowStartFgsState(PROCESS_STATE_BOUND_TOP);
+                                state.bumpAllowStartFgsState(PROCESS_STATE_BOUND_TOP);
                                 boolean enabled = false;
                                 try {
                                     enabled = mPlatformCompatCache.isChangeEnabled(
@@ -1969,7 +2038,7 @@
 
                         if (procState > clientProcState) {
                             procState = clientProcState;
-                            app.setCurRawProcState(procState);
+                            state.setCurRawProcState(procState);
                             if (adjType == null) {
                                 adjType = "service";
                             }
@@ -1979,12 +2048,12 @@
                             app.setPendingUiClean(true);
                         }
                         if (adjType != null) {
-                            app.adjType = adjType;
-                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                    .REASON_SERVICE_IN_USE;
-                            app.adjSource = cr.binding.client;
-                            app.adjSourceProcState = clientProcState;
-                            app.adjTarget = s.instanceName;
+                            state.setAdjType(adjType);
+                            state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
+                                    .REASON_SERVICE_IN_USE);
+                            state.setAdjSource(cr.binding.client);
+                            state.setAdjSourceProcState(clientProcState);
+                            state.setAdjTarget(s.instanceName);
                             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
                                         + ": " + app + ", due to " + cr.binding.client
@@ -2003,18 +2072,18 @@
                         // bound by an unfrozen app via a WPRI binding has to remain
                         // unfrozen.
                         if (clientAdj < ProcessList.CACHED_APP_MIN_ADJ) {
-                            app.shouldNotFreeze = true;
+                            app.mOptRecord.setShouldNotFreeze(true);
                         }
                     }
                     if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
-                        app.treatLikeActivity = true;
+                        psr.setTreatLikeActivity(true);
                     }
                     final ActivityServiceConnectionsHolder a = cr.activity;
                     if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
                         if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
                                 && a.isActivityVisible()) {
                             adj = ProcessList.FOREGROUND_APP_ADJ;
-                            app.setCurRawAdj(adj);
+                            state.setCurRawAdj(adj);
                             if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
                                 if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
                                     schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
@@ -2022,13 +2091,13 @@
                                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                                 }
                             }
-                            app.setCached(false);
-                            app.adjType = "service";
-                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                    .REASON_SERVICE_IN_USE;
-                            app.adjSource = a;
-                            app.adjSourceProcState = procState;
-                            app.adjTarget = s.instanceName;
+                            state.setCached(false);
+                            state.setAdjType("service");
+                            state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
+                                    .REASON_SERVICE_IN_USE);
+                            state.setAdjSource(a);
+                            state.setAdjSourceProcState(procState);
+                            state.setAdjTarget(s.instanceName);
                             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                                 reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                         "Raise to service w/activity: " + app);
@@ -2039,12 +2108,13 @@
             }
         }
 
-        for (int provi = app.pubProviders.size() - 1;
+        final ProcessProviderRecord ppr = app.mProviders;
+        for (int provi = ppr.numberOfProviders() - 1;
                 provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                         || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                         || procState > PROCESS_STATE_TOP);
                 provi--) {
-            ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
+            ContentProviderRecord cpr = ppr.getProviderAt(provi);
             for (int i = cpr.connections.size() - 1;
                     i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                             || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
@@ -2052,24 +2122,24 @@
                     i--) {
                 ContentProviderConnection conn = cpr.connections.get(i);
                 ProcessRecord client = conn.client;
+                final ProcessStateRecord cstate = client.mState;
                 if (client == app) {
                     // Being our own client is not interesting.
                     continue;
                 }
                 if (computeClients) {
-                    computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now, cycleReEval,
-                            true);
+                    computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true);
                 } else {
-                    client.setCurRawAdj(client.setAdj);
-                    client.setCurRawProcState(client.setProcState);
+                    cstate.setCurRawAdj(cstate.getSetAdj());
+                    cstate.setCurRawProcState(cstate.getSetProcState());
                 }
 
-                if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                if (shouldSkipDueToCycle(state, cstate, procState, adj, cycleReEval)) {
                     continue;
                 }
 
-                int clientAdj = client.getCurRawAdj();
-                int clientProcState = client.getCurRawProcState();
+                int clientAdj = cstate.getCurRawAdj();
+                int clientProcState = cstate.getCurRawProcState();
 
                 if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                     // If the other app is cached for any reason, for purposes here
@@ -2078,16 +2148,16 @@
                 }
                 String adjType = null;
                 if (adj > clientAdj) {
-                    if (app.hasShownUi && !app.getCachedIsHomeProcess()
+                    if (state.hasShownUi() && !state.getCachedIsHomeProcess()
                             && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                         adjType = "cch-ui-provider";
                     } else {
                         adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
                                 ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
-                        app.setCurRawAdj(adj);
+                        state.setCurRawAdj(adj);
                         adjType = "provider";
                     }
-                    app.setCached(app.isCached() & client.isCached());
+                    state.setCached(state.isCached() & cstate.isCached());
                 }
 
                 if (clientProcState <= PROCESS_STATE_FOREGROUND_SERVICE) {
@@ -2104,18 +2174,18 @@
                 conn.trackProcState(clientProcState, mAdjSeq, now);
                 if (procState > clientProcState) {
                     procState = clientProcState;
-                    app.setCurRawProcState(procState);
+                    state.setCurRawProcState(procState);
                 }
-                if (client.getCurrentSchedulingGroup() > schedGroup) {
+                if (cstate.getCurrentSchedulingGroup() > schedGroup) {
                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                 }
                 if (adjType != null) {
-                    app.adjType = adjType;
-                    app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                            .REASON_PROVIDER_IN_USE;
-                    app.adjSource = client;
-                    app.adjSourceProcState = clientProcState;
-                    app.adjTarget = cpr.name;
+                    state.setAdjType(adjType);
+                    state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
+                            .REASON_PROVIDER_IN_USE);
+                    state.setAdjSource(client);
+                    state.setAdjSourceProcState(clientProcState);
+                    state.setAdjTarget(cpr.name);
                     if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                         reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
                                 + ": " + app + ", due to " + client
@@ -2130,11 +2200,11 @@
             if (cpr.hasExternalProcessHandles()) {
                 if (adj > ProcessList.FOREGROUND_APP_ADJ) {
                     adj = ProcessList.FOREGROUND_APP_ADJ;
-                    app.setCurRawAdj(adj);
+                    state.setCurRawAdj(adj);
                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                    app.setCached(false);
-                    app.adjType = "ext-provider";
-                    app.adjTarget = cpr.name;
+                    state.setCached(false);
+                    state.setAdjType("ext-provider");
+                    state.setAdjTarget(cpr.name);
                     if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                         reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                 "Raise adj to external provider: " + app);
@@ -2142,7 +2212,7 @@
                 }
                 if (procState > PROCESS_STATE_IMPORTANT_FOREGROUND) {
                     procState = PROCESS_STATE_IMPORTANT_FOREGROUND;
-                    app.setCurRawProcState(procState);
+                    state.setCurRawProcState(procState);
                     if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                         reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                 "Raise procstate to external provider: " + app);
@@ -2151,13 +2221,13 @@
             }
         }
 
-        if (app.lastProviderTime > 0 &&
-                (app.lastProviderTime + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
+        if (ppr.getLastProviderTime() > 0
+                && (ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
             if (adj > ProcessList.PREVIOUS_APP_ADJ) {
                 adj = ProcessList.PREVIOUS_APP_ADJ;
                 schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.setCached(false);
-                app.adjType = "recent-provider";
+                state.setCached(false);
+                state.setAdjType("recent-provider");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ,
                             "Raise adj to recent provider: " + app);
@@ -2165,7 +2235,7 @@
             }
             if (procState > PROCESS_STATE_LAST_ACTIVITY) {
                 procState = PROCESS_STATE_LAST_ACTIVITY;
-                app.adjType = "recent-provider";
+                state.setAdjType("recent-provider");
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ,
                             "Raise procstate to recent provider: " + app);
@@ -2174,24 +2244,23 @@
         }
 
         if (procState >= PROCESS_STATE_CACHED_EMPTY) {
-            if (app.hasClientActivities()) {
+            if (psr.hasClientActivities()) {
                 // This is a cached process, but with client activities.  Mark it so.
                 procState = PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
-                app.adjType = "cch-client-act";
-            } else if (app.treatLikeActivity) {
+                state.setAdjType("cch-client-act");
+            } else if (psr.isTreatedLikeActivity()) {
                 // This is a cached process, but somebody wants us to treat it like it has
                 // an activity, okay!
                 procState = PROCESS_STATE_CACHED_ACTIVITY;
-                app.adjType = "cch-as-act";
+                state.setAdjType("cch-as-act");
             }
         }
 
         if (adj == ProcessList.SERVICE_ADJ) {
             if (doingAll && !cycleReEval) {
-                app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
+                state.setServiceB(mNewNumAServiceProcs > (mNumServiceProcs / 3));
                 mNewNumServiceProcs++;
-                //Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb);
-                if (!app.serviceb) {
+                if (!state.isServiceB()) {
                     // This service isn't far enough down on the LRU list to
                     // normally be a B service, but if we are low on RAM and it
                     // is large we want to force it down since we would prefer to
@@ -2199,29 +2268,27 @@
                     if (!mService.mAppProfiler.isLastMemoryLevelNormal()
                             && app.mProfile.getLastPss()
                             >= mProcessList.getCachedRestoreThresholdKb()) {
-                        app.serviceHighRam = true;
-                        app.serviceb = true;
+                        state.setServiceHighRam(true);
+                        state.setServiceB(true);
                         //Slog.i(TAG, "ADJ " + app + " high ram!");
                     } else {
                         mNewNumAServiceProcs++;
                         //Slog.i(TAG, "ADJ " + app + " not high ram!");
                     }
                 } else {
-                    app.serviceHighRam = false;
+                    state.setServiceHighRam(false);
                 }
             }
-            if (app.serviceb) {
+            if (state.isServiceB()) {
                 adj = ProcessList.SERVICE_B_ADJ;
             }
         }
 
-        app.setCurRawAdj(adj);
+        state.setCurRawAdj(adj);
 
-        //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid +
-        //      " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);
-        if (adj > app.maxAdj) {
-            adj = app.maxAdj;
-            if (app.maxAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+        if (adj > state.getMaxAdj()) {
+            adj = state.getMaxAdj();
+            if (adj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
                 schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
             }
         }
@@ -2236,31 +2303,32 @@
         }
 
         // apply capability from FGS.
-        if (app.hasForegroundServices()) {
+        if (psr.hasForegroundServices()) {
             capability |= capabilityFromFGS;
         }
 
-        capability |= getDefaultCapability(app, procState);
+        capability |= getDefaultCapability(psr, procState);
 
         // Do final modification to adj.  Everything we do between here and applying
         // the final setAdj must be done in this function, because we will also use
         // it when computing the final cached adj later.  Note that we don't need to
         // worry about this for max adj above, since max adj will always be used to
         // keep it out of the cached vaues.
-        app.curAdj = app.modifyRawOomAdj(adj);
-        app.curCapability = capability;
-        app.setCurrentSchedulingGroup(schedGroup);
-        app.setCurProcState(procState);
-        app.setCurRawProcState(procState);
-        app.setHasForegroundActivities(foregroundActivities);
-        app.completedAdjSeq = mAdjSeq;
-        app.setAllowStartFgs();
+        state.setCurAdj(psr.modifyRawOomAdj(adj));
+        state.setCurCapability(capability);
+        state.setCurrentSchedulingGroup(schedGroup);
+        state.setCurProcState(procState);
+        state.setCurRawProcState(procState);
+        state.setHasForegroundActivities(foregroundActivities);
+        state.setCompletedAdjSeq(mAdjSeq);
+        state.setAllowStartFgs();
+
         // if curAdj or curProcState improved, then this process was promoted
-        return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState
-                || app.curCapability != prevCapability ;
+        return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState
+                || state.getCurCapability() != prevCapability;
     }
 
-    private int getDefaultCapability(ProcessRecord app, int procState) {
+    private int getDefaultCapability(ProcessServiceRecord psr, int procState) {
         switch (procState) {
             case PROCESS_STATE_PERSISTENT:
             case PROCESS_STATE_PERSISTENT_UI:
@@ -2269,7 +2337,7 @@
             case PROCESS_STATE_BOUND_TOP:
                 return PROCESS_CAPABILITY_NONE;
             case PROCESS_STATE_FOREGROUND_SERVICE:
-                if (app.hasForegroundServices()) {
+                if (psr.hasForegroundServices()) {
                     // Capability from FGS are conditional depending on foreground service type in
                     // manifest file and the mAllowWhileInUsePermissionInFgs flag.
                     return PROCESS_CAPABILITY_NONE;
@@ -2297,16 +2365,16 @@
      *                    evaluation.
      * @return whether to skip using the client connection at this time
      */
-    private boolean shouldSkipDueToCycle(ProcessRecord app, ProcessRecord client,
+    private boolean shouldSkipDueToCycle(ProcessStateRecord app, ProcessStateRecord client,
             int procState, int adj, boolean cycleReEval) {
-        if (client.containsCycle) {
-            // We've detected a cycle. We should retry computeOomAdjLocked later in
+        if (client.containsCycle()) {
+            // We've detected a cycle. We should retry computeOomAdjLSP later in
             // case a later-checked connection from a client  would raise its
             // priority legitimately.
-            app.containsCycle = true;
+            app.setContainsCycle(true);
             // If the client has not been completely evaluated, check if it's worth
             // using the partial values.
-            if (client.completedAdjSeq < mAdjSeq) {
+            if (client.getCompletedAdjSeq() < mAdjSeq) {
                 if (cycleReEval) {
                     // If the partial values are no better, skip until the next
                     // attempt
@@ -2325,7 +2393,8 @@
     }
 
     /** Inform the oomadj observer of changes to oomadj. Used by tests. */
-    void reportOomAdjMessageLocked(String tag, String msg) {
+    @GuardedBy("mService")
+    private void reportOomAdjMessageLocked(String tag, String msg) {
         Slog.d(tag, msg);
         synchronized (mService.mOomAdjObserverLock) {
             if (mService.mCurOomAdjObserver != null) {
@@ -2336,13 +2405,14 @@
     }
 
     /** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
-    @GuardedBy("mService")
-    private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
+    @GuardedBy({"mService", "mProcLock"})
+    private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
             long nowElapsed) {
         boolean success = true;
+        final ProcessStateRecord state = app.mState;
 
-        if (app.getCurRawAdj() != app.setRawAdj) {
-            app.setRawAdj = app.getCurRawAdj();
+        if (state.getCurRawAdj() != state.getSetRawAdj()) {
+            state.setSetRawAdj(state.getCurRawAdj());
         }
 
         int changes = 0;
@@ -2350,22 +2420,22 @@
         // don't compact during bootup
         if (mCachedAppOptimizer.useCompaction() && mService.mBooted) {
             // Cached and prev/home compaction
-            if (app.curAdj != app.setAdj) {
+            if (state.getCurAdj() != state.getSetAdj()) {
                 // Perform a minor compaction when a perceptible app becomes the prev/home app
                 // Perform a major compaction when any app enters cached
                 // reminder: here, setAdj is previous state, curAdj is upcoming state
-                if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
-                        (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
-                                app.curAdj == ProcessList.HOME_APP_ADJ)) {
+                if (state.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ
+                        && (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ
+                            || state.getCurAdj() == ProcessList.HOME_APP_ADJ)) {
                     mCachedAppOptimizer.compactAppSome(app);
-                } else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
-                                || app.setAdj > ProcessList.CACHED_APP_MAX_ADJ)
-                        && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
-                        && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
+                } else if ((state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ
+                                || state.getSetAdj() > ProcessList.CACHED_APP_MAX_ADJ)
+                        && state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ
+                        && state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) {
                     mCachedAppOptimizer.compactAppFull(app);
                 }
             } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
-                    && app.setAdj < ProcessList.FOREGROUND_APP_ADJ
+                    && state.getSetAdj() < ProcessList.FOREGROUND_APP_ADJ
                     // Because these can fire independent of oom_adj/procstate changes, we need
                     // to throttle the actual dispatch of these requests in addition to the
                     // processing of the requests. As a result, there is throttling both here
@@ -2373,36 +2443,36 @@
                     && mCachedAppOptimizer.shouldCompactPersistent(app, now)) {
                 mCachedAppOptimizer.compactAppPersistent(app);
             } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
-                    && app.getCurProcState()
+                    && state.getCurProcState()
                         == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
                     && mCachedAppOptimizer.shouldCompactBFGS(app, now)) {
                 mCachedAppOptimizer.compactAppBfgs(app);
             }
         }
 
-        if (app.curAdj != app.setAdj) {
-            ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
+        if (state.getCurAdj() != state.getSetAdj()) {
+            ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
-                String msg = "Set " + app.pid + " " + app.processName + " adj "
-                        + app.curAdj + ": " + app.adjType;
+                String msg = "Set " + app.getPid() + " " + app.processName + " adj "
+                        + state.getCurAdj() + ": " + state.getAdjType();
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
             }
-            app.setAdj = app.curAdj;
-            app.verifiedAdj = ProcessList.INVALID_ADJ;
+            state.setSetAdj(state.getCurAdj());
+            state.setVerifiedAdj(ProcessList.INVALID_ADJ);
         }
 
-        final int curSchedGroup = app.getCurrentSchedulingGroup();
-        if (app.setSchedGroup != curSchedGroup) {
-            int oldSchedGroup = app.setSchedGroup;
-            app.setSchedGroup = curSchedGroup;
+        final int curSchedGroup = state.getCurrentSchedulingGroup();
+        if (state.getSetSchedGroup() != curSchedGroup) {
+            int oldSchedGroup = state.getSetSchedGroup();
+            state.setSetSchedGroup(curSchedGroup);
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
                 String msg = "Setting sched group of " + app.processName
-                        + " to " + curSchedGroup + ": " + app.adjType;
+                        + " to " + curSchedGroup + ": " + state.getAdjType();
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
             }
-            if (app.waitingToKill != null && app.curReceivers.isEmpty()
-                    && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
-                app.kill(app.waitingToKill, ApplicationExitInfo.REASON_USER_REQUESTED,
+            if (app.getWaitingToKill() != null && app.mReceivers.numberOfCurReceivers() == 0
+                    && state.getSetSchedGroup() == ProcessList.SCHED_GROUP_BACKGROUND) {
+                app.killLocked(app.getWaitingToKill(), ApplicationExitInfo.REASON_USER_REQUESTED,
                         ApplicationExitInfo.SUBREASON_UNKNOWN, true);
                 success = false;
             } else {
@@ -2423,22 +2493,23 @@
                         break;
                 }
                 mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
-                        0 /* unused */, app.pid, processGroup, app.processName));
+                        0 /* unused */, app.getPid(), processGroup, app.processName));
                 try {
+                    final int renderThreadTid = app.getRenderThreadTid();
                     if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
                         // do nothing if we already switched to RT
                         if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
                             app.getWindowProcessController().onTopProcChanged();
                             if (mService.mUseFifoUiScheduling) {
                                 // Switch UI pipeline for app to SCHED_FIFO
-                                app.savedPriority = Process.getThreadPriority(app.pid);
-                                mService.scheduleAsFifoPriority(app.pid, /* suppressLogs */true);
-                                if (app.renderThreadTid != 0) {
-                                    mService.scheduleAsFifoPriority(app.renderThreadTid,
+                                state.setSavedPriority(Process.getThreadPriority(app.getPid()));
+                                mService.scheduleAsFifoPriority(app.getPid(), true);
+                                if (renderThreadTid != 0) {
+                                    mService.scheduleAsFifoPriority(renderThreadTid,
                                             /* suppressLogs */true);
                                     if (DEBUG_OOM_ADJ) {
                                         Slog.d("UI_FIFO", "Set RenderThread (TID " +
-                                                app.renderThreadTid + ") to FIFO");
+                                                renderThreadTid + ") to FIFO");
                                     }
                                 } else {
                                     if (DEBUG_OOM_ADJ) {
@@ -2447,10 +2518,10 @@
                                 }
                             } else {
                                 // Boost priority for top app UI and render threads
-                                setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);
-                                if (app.renderThreadTid != 0) {
+                                setThreadPriority(app.getPid(), TOP_APP_PRIORITY_BOOST);
+                                if (renderThreadTid != 0) {
                                     try {
-                                        setThreadPriority(app.renderThreadTid,
+                                        setThreadPriority(renderThreadTid,
                                                 TOP_APP_PRIORITY_BOOST);
                                     } catch (IllegalArgumentException e) {
                                         // thread died, ignore
@@ -2464,10 +2535,10 @@
                         if (mService.mUseFifoUiScheduling) {
                             try {
                                 // Reset UI pipeline to SCHED_OTHER
-                                setThreadScheduler(app.pid, SCHED_OTHER, 0);
-                                setThreadPriority(app.pid, app.savedPriority);
-                                if (app.renderThreadTid != 0) {
-                                    setThreadScheduler(app.renderThreadTid,
+                                setThreadScheduler(app.getPid(), SCHED_OTHER, 0);
+                                setThreadPriority(app.getPid(), state.getSavedPriority());
+                                if (renderThreadTid != 0) {
+                                    setThreadScheduler(renderThreadTid,
                                             SCHED_OTHER, 0);
                                 }
                             } catch (IllegalArgumentException e) {
@@ -2479,139 +2550,141 @@
                             }
                         } else {
                             // Reset priority for top app UI and render threads
-                            setThreadPriority(app.pid, 0);
+                            setThreadPriority(app.getPid(), 0);
                         }
 
-                        if (app.renderThreadTid != 0) {
-                            setThreadPriority(app.renderThreadTid, THREAD_PRIORITY_DISPLAY);
+                        if (renderThreadTid != 0) {
+                            setThreadPriority(renderThreadTid, THREAD_PRIORITY_DISPLAY);
                         }
                     }
                 } catch (Exception e) {
                     if (DEBUG_ALL) {
-                        Slog.w(TAG, "Failed setting thread priority of " + app.pid, e);
+                        Slog.w(TAG, "Failed setting thread priority of " + app.getPid(), e);
                     }
                 }
             }
         }
-        if (app.repForegroundActivities != app.hasForegroundActivities()) {
-            app.repForegroundActivities = app.hasForegroundActivities();
+        if (state.hasRepForegroundActivities() != state.hasForegroundActivities()) {
+            state.setRepForegroundActivities(state.hasForegroundActivities());
             changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES;
         }
 
-        updateAppFreezeStateLocked(app);
+        updateAppFreezeStateLSP(app);
 
-        if (app.getReportedProcState() != app.getCurProcState()) {
-            app.setReportedProcState(app.getCurProcState());
-            if (app.thread != null) {
+        if (state.getReportedProcState() != state.getCurProcState()) {
+            state.setReportedProcState(state.getCurProcState());
+            if (app.getThread() != null) {
                 try {
                     if (false) {
                         //RuntimeException h = new RuntimeException("here");
-                        Slog.i(TAG, "Sending new process state " + app.getReportedProcState()
+                        Slog.i(TAG, "Sending new process state " + state.getReportedProcState()
                                 + " to " + app /*, h*/);
                     }
-                    app.thread.setProcessState(app.getReportedProcState());
+                    app.getThread().setProcessState(state.getReportedProcState());
                 } catch (RemoteException e) {
                 }
             }
         }
         boolean forceUpdatePssTime = false;
-        if (app.setProcState == PROCESS_STATE_NONEXISTENT
-                || ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) {
-            app.lastStateTime = now;
+        if (state.getSetProcState() == PROCESS_STATE_NONEXISTENT
+                || ProcessList.procStatesDifferForMem(
+                        state.getCurProcState(), state.getSetProcState())) {
+            state.setLastStateTime(now);
             forceUpdatePssTime = true;
             if (DEBUG_PSS) {
                 Slog.d(TAG_PSS, "Process state change from "
-                        + ProcessList.makeProcStateString(app.setProcState) + " to "
-                        + ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
+                        + ProcessList.makeProcStateString(state.getSetProcState()) + " to "
+                        + ProcessList.makeProcStateString(state.getCurProcState()) + " next pss in "
                         + (app.mProfile.getNextPssTime() - now) + ": " + app);
             }
         }
         synchronized (mService.mAppProfiler.mProfilerLock) {
-            app.mProfile.updateProcState(app);
+            app.mProfile.updateProcState(app.mState);
             mService.mAppProfiler.updateNextPssTimeLPf(
-                    app.getCurProcState(), app.mProfile, now, forceUpdatePssTime);
+                    state.getCurProcState(), app.mProfile, now, forceUpdatePssTime);
         }
-        if (app.setProcState != app.getCurProcState()) {
+        if (state.getSetProcState() != state.getCurProcState()) {
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
                 String msg = "Proc state change of " + app.processName
-                        + " to " + ProcessList.makeProcStateString(app.getCurProcState())
-                        + " (" + app.getCurProcState() + ")" + ": " + app.adjType;
+                        + " to " + ProcessList.makeProcStateString(state.getCurProcState())
+                        + " (" + state.getCurProcState() + ")" + ": " + state.getAdjType();
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
             }
-            boolean setImportant = app.setProcState < PROCESS_STATE_SERVICE;
-            boolean curImportant = app.getCurProcState() < PROCESS_STATE_SERVICE;
+            boolean setImportant = state.getSetProcState() < PROCESS_STATE_SERVICE;
+            boolean curImportant = state.getCurProcState() < PROCESS_STATE_SERVICE;
             if (setImportant && !curImportant) {
                 // This app is no longer something we consider important enough to allow to use
                 // arbitrary amounts of battery power. Note its current CPU time to later know to
                 // kill it if it is not behaving well.
-                app.setWhenUnimportant(now);
+                state.setWhenUnimportant(now);
                 app.mProfile.mLastCpuTime.set(0);
             }
             // Inform UsageStats of important process state change
             // Must be called before updating setProcState
-            maybeUpdateUsageStatsLocked(app, nowElapsed);
+            maybeUpdateUsageStatsLSP(app, nowElapsed);
 
-            maybeUpdateLastTopTime(app, now);
+            maybeUpdateLastTopTime(state, now);
 
-            app.setProcState = app.getCurProcState();
-            if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
-                app.notCachedSinceIdle = false;
+            state.setSetProcState(state.getCurProcState());
+            if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) {
+                state.setNotCachedSinceIdle(false);
             }
             if (!doingAll) {
-                mService.setProcessTrackerStateLocked(app,
+                mService.setProcessTrackerStateLOSP(app,
                         mService.mProcessStats.getMemFactorLocked(), now);
             } else {
-                app.procStateChanged = true;
+                state.setProcStateChanged(true);
             }
-        } else if (app.reportedInteraction && (nowElapsed - app.getInteractionEventTime())
+        } else if (state.hasReportedInteraction() && (nowElapsed - state.getInteractionEventTime())
                 > mConstants.USAGE_STATS_INTERACTION_INTERVAL) {
             // For apps that sit around for a long time in the interactive state, we need
             // to report this at least once a day so they don't go idle.
-            maybeUpdateUsageStatsLocked(app, nowElapsed);
-        } else if (!app.reportedInteraction && (nowElapsed - app.getFgInteractionTime())
+            maybeUpdateUsageStatsLSP(app, nowElapsed);
+        } else if (!state.hasReportedInteraction() && (nowElapsed - state.getFgInteractionTime())
                 > mConstants.SERVICE_USAGE_INTERACTION_TIME) {
             // For foreground services that sit around for a long time but are not interacted with.
-            maybeUpdateUsageStatsLocked(app, nowElapsed);
+            maybeUpdateUsageStatsLSP(app, nowElapsed);
         }
 
-        if (app.curCapability != app.setCapability) {
+        if (state.getCurCapability() != state.getSetCapability()) {
             changes |= ActivityManagerService.ProcessChangeItem.CHANGE_CAPABILITY;
-            app.setCapability = app.curCapability;
+            state.setSetCapability(state.getCurCapability());
         }
 
         if (changes != 0) {
             if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
                     "Changes in " + app + ": " + changes);
             ActivityManagerService.ProcessChangeItem item =
-                    mProcessList.enqueueProcessChangeItemLocked(app.pid, app.info.uid);
+                    mProcessList.enqueueProcessChangeItemLocked(app.getPid(), app.info.uid);
             item.changes |= changes;
-            item.foregroundActivities = app.repForegroundActivities;
-            item.capability = app.setCapability;
+            item.foregroundActivities = state.hasRepForegroundActivities();
+            item.capability = state.getSetCapability();
             if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
                     "Item " + Integer.toHexString(System.identityHashCode(item))
                             + " " + app.toShortString() + ": changes=" + item.changes
                             + " foreground=" + item.foregroundActivities
-                            + " type=" + app.adjType + " source=" + app.adjSource
-                            + " target=" + app.adjTarget + " capability=" + item.capability);
+                            + " type=" + state.getAdjType() + " source=" + state.getAdjSource()
+                            + " target=" + state.getAdjTarget() + " capability=" + item.capability);
         }
 
         return success;
     }
 
-    @GuardedBy("mService")
-    void setAttachingSchedGroupLocked(ProcessRecord app) {
+    @GuardedBy({"mService", "mProcLock"})
+    void setAttachingSchedGroupLSP(ProcessRecord app) {
         int initialSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+        final ProcessStateRecord state = app.mState;
         // If the process has been marked as foreground via Zygote.START_FLAG_USE_TOP_APP_PRIORITY,
         // then verify that the top priority is actually is applied.
-        if (app.hasForegroundActivities()) {
+        if (state.hasForegroundActivities()) {
             String fallbackReason = null;
             try {
-                // The priority must be the same as how does {@link #applyOomAdjLocked} set for
+                // The priority must be the same as how does {@link #applyOomAdjLSP} set for
                 // {@link ProcessList.SCHED_GROUP_TOP_APP}. We don't check render thread because it
                 // is not ready when attaching.
-                if (Process.getProcessGroup(app.pid) == THREAD_GROUP_TOP_APP) {
+                if (Process.getProcessGroup(app.getPid()) == THREAD_GROUP_TOP_APP) {
                     app.getWindowProcessController().onTopProcChanged();
-                    setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);
+                    setThreadPriority(app.getPid(), TOP_APP_PRIORITY_BOOST);
                 } else {
                     fallbackReason = "not expected top priority";
                 }
@@ -2627,23 +2700,27 @@
             }
         }
 
-        app.setCurrentSchedulingGroup(app.setSchedGroup = initialSchedGroup);
+        state.setSetSchedGroup(initialSchedGroup);
+        state.setCurrentSchedulingGroup(initialSchedGroup);
     }
 
     // ONLY used for unit testing in OomAdjusterTests.java
     @VisibleForTesting
     void maybeUpdateUsageStats(ProcessRecord app, long nowElapsed) {
         synchronized (mService) {
-            maybeUpdateUsageStatsLocked(app, nowElapsed);
+            synchronized (mProcLock) {
+                maybeUpdateUsageStatsLSP(app, nowElapsed);
+            }
         }
     }
 
-    @GuardedBy("mService")
-    private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) {
+    @GuardedBy({"mService", "mProcLock"})
+    private void maybeUpdateUsageStatsLSP(ProcessRecord app, long nowElapsed) {
+        final ProcessStateRecord state = app.mState;
         if (DEBUG_USAGE_STATS) {
             Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
-                    + "] state changes: old = " + app.setProcState + ", new = "
-                    + app.getCurProcState());
+                    + "] state changes: old = " + state.getSetProcState() + ", new = "
+                    + state.getCurProcState());
         }
         if (mService.mUsageStatsService == null) {
             return;
@@ -2652,27 +2729,28 @@
         // To avoid some abuse patterns, we are going to be careful about what we consider
         // to be an app interaction.  Being the top activity doesn't count while the display
         // is sleeping, nor do short foreground services.
-        if (app.getCurProcState() <= PROCESS_STATE_TOP
-                || app.getCurProcState() == PROCESS_STATE_BOUND_TOP) {
+        if (state.getCurProcState() <= PROCESS_STATE_TOP
+                || state.getCurProcState() == PROCESS_STATE_BOUND_TOP) {
             isInteraction = true;
-            app.setFgInteractionTime(0);
-        } else if (app.getCurProcState() <= PROCESS_STATE_FOREGROUND_SERVICE) {
-            if (app.getFgInteractionTime() == 0) {
-                app.setFgInteractionTime(nowElapsed);
+            state.setFgInteractionTime(0);
+        } else if (state.getCurProcState() <= PROCESS_STATE_FOREGROUND_SERVICE) {
+            if (state.getFgInteractionTime() == 0) {
+                state.setFgInteractionTime(nowElapsed);
                 isInteraction = false;
             } else {
-                isInteraction = nowElapsed > app.getFgInteractionTime()
+                isInteraction = nowElapsed > state.getFgInteractionTime()
                         + mConstants.SERVICE_USAGE_INTERACTION_TIME;
             }
         } else {
             isInteraction =
-                    app.getCurProcState() <= PROCESS_STATE_IMPORTANT_FOREGROUND;
-            app.setFgInteractionTime(0);
+                    state.getCurProcState() <= PROCESS_STATE_IMPORTANT_FOREGROUND;
+            state.setFgInteractionTime(0);
         }
         if (isInteraction
-                && (!app.reportedInteraction || (nowElapsed - app.getInteractionEventTime())
-                > mConstants.USAGE_STATS_INTERACTION_INTERVAL)) {
-            app.setInteractionEventTime(nowElapsed);
+                && (!state.hasReportedInteraction()
+                    || (nowElapsed - state.getInteractionEventTime())
+                    > mConstants.USAGE_STATS_INTERACTION_INTERVAL)) {
+            state.setInteractionEventTime(nowElapsed);
             String[] packages = app.getPackageList();
             if (packages != null) {
                 for (int i = 0; i < packages.length; i++) {
@@ -2681,16 +2759,16 @@
                 }
             }
         }
-        app.reportedInteraction = isInteraction;
+        state.setReportedInteraction(isInteraction);
         if (!isInteraction) {
-            app.setInteractionEventTime(0);
+            state.setInteractionEventTime(0);
         }
     }
 
-    private void maybeUpdateLastTopTime(ProcessRecord app, long nowUptime) {
-        if (app.setProcState <= PROCESS_STATE_TOP
-                && app.getCurProcState() > PROCESS_STATE_TOP) {
-            app.lastTopTime = nowUptime;
+    private void maybeUpdateLastTopTime(ProcessStateRecord state, long nowUptime) {
+        if (state.getSetProcState() <= PROCESS_STATE_TOP
+                && state.getCurProcState() > PROCESS_STATE_TOP) {
+            state.setLastTopTime(nowUptime);
         }
     }
 
@@ -2712,13 +2790,15 @@
         }
         for (int i = N - 1; i >= 0; i--) {
             final UidRecord uidRec = mActiveUids.valueAt(i);
-            final long bgTime = uidRec.lastBackgroundTime;
-            if (bgTime > 0 && !uidRec.idle) {
+            final long bgTime = uidRec.getLastBackgroundTime();
+            if (bgTime > 0 && !uidRec.isIdle()) {
                 if (bgTime <= maxBgTime) {
-                    EventLogTags.writeAmUidIdle(uidRec.uid);
-                    uidRec.idle = true;
-                    uidRec.setIdle = true;
-                    mService.doStopUidLocked(uidRec.uid, uidRec);
+                    EventLogTags.writeAmUidIdle(uidRec.getUid());
+                    synchronized (mProcLock) {
+                        uidRec.setIdle(true);
+                        uidRec.setSetIdle(true);
+                    }
+                    mService.doStopUidLocked(uidRec.getUid(), uidRec);
                 } else {
                     if (nextTime == 0 || nextTime > bgTime) {
                         nextTime = bgTime;
@@ -2736,35 +2816,35 @@
         }
     }
 
-    @GuardedBy("mService")
-    void setAppIdTempAllowlistStateLocked(int uid, boolean onAllowlist) {
+    @GuardedBy({"mService", "mProcLock"})
+    void setAppIdTempAllowlistStateLSP(int uid, boolean onAllowlist) {
         boolean changed = false;
         for (int i = mActiveUids.size() - 1; i >= 0; i--) {
             final UidRecord uidRec = mActiveUids.valueAt(i);
-            if (uidRec.uid == uid && uidRec.mCurAllowlist != onAllowlist) {
-                uidRec.mCurAllowlist = onAllowlist;
+            if (uidRec.getUid() == uid && uidRec.isCurAllowListed() != onAllowlist) {
+                uidRec.setCurAllowListed(onAllowlist);
                 changed = true;
             }
         }
         if (changed) {
-            updateOomAdjLocked(OOM_ADJ_REASON_ALLOWLIST);
+            updateOomAdjLSP(OOM_ADJ_REASON_ALLOWLIST);
         }
     }
 
-    @GuardedBy("mService")
-    void setUidTempAllowlistStateLocked(int uid, boolean onAllowlist) {
+    @GuardedBy({"mService", "mProcLock"})
+    void setUidTempAllowlistStateLSP(int uid, boolean onAllowlist) {
         boolean changed = false;
         final UidRecord uidRec = mActiveUids.get(uid);
-        if (uidRec != null && uidRec.mCurAllowlist != onAllowlist) {
-            uidRec.mCurAllowlist = onAllowlist;
-            updateOomAdjLocked(OOM_ADJ_REASON_ALLOWLIST);
+        if (uidRec != null && uidRec.isCurAllowListed() != onAllowlist) {
+            uidRec.setCurAllowListed(onAllowlist);
+            updateOomAdjLSP(OOM_ADJ_REASON_ALLOWLIST);
         }
     }
 
     @GuardedBy("mService")
     void dumpProcessListVariablesLocked(ProtoOutputStream proto) {
         proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
-        proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mProcessList.mLruSeq);
+        proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mProcessList.getLruSeqLOSP());
         proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS,
                 mNumNonCachedProcs);
         proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
@@ -2775,19 +2855,19 @@
 
     @GuardedBy("mService")
     void dumpSequenceNumbersLocked(PrintWriter pw) {
-        pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.mLruSeq);
+        pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.getLruSeqLOSP());
     }
 
     @GuardedBy("mService")
     void dumpProcCountsLocked(PrintWriter pw) {
         pw.println("  mNumNonCachedProcs=" + mNumNonCachedProcs
-                + " (" + mProcessList.getLruSizeLocked() + " total)"
+                + " (" + mProcessList.getLruSizeLOSP() + " total)"
                 + " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
                 + " mNumServiceProcs=" + mNumServiceProcs
                 + " mNewNumServiceProcs=" + mNewNumServiceProcs);
     }
 
-    @GuardedBy("mService")
+    @GuardedBy("mProcLock")
     void dumpCachedAppOptimizerSettings(PrintWriter pw) {
         mCachedAppOptimizer.dump(pw);
     }
@@ -2797,22 +2877,25 @@
         mCacheOomRanker.dump(pw);
     }
 
-    @GuardedBy("mService")
-    void updateAppFreezeStateLocked(ProcessRecord app) {
+    @GuardedBy({"mService", "mProcLock"})
+    private void updateAppFreezeStateLSP(ProcessRecord app) {
         if (!mCachedAppOptimizer.useFreezer()) {
             return;
         }
 
+        final ProcessCachedOptimizerRecord opt = app.mOptRecord;
         // if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze
-        if (app.frozen && app.shouldNotFreeze) {
-            mCachedAppOptimizer.unfreezeAppLocked(app);
+        if (opt.isFrozen() && opt.shouldNotFreeze()) {
+            mCachedAppOptimizer.unfreezeAppLSP(app);
         }
 
+        final ProcessStateRecord state = app.mState;
         // Use current adjustment when freezing, set adjustment when unfreezing.
-        if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && !app.frozen && !app.shouldNotFreeze) {
-            mCachedAppOptimizer.freezeAppAsync(app);
-        } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ && app.frozen) {
-            mCachedAppOptimizer.unfreezeAppLocked(app);
+        if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()
+                && !opt.shouldNotFreeze()) {
+            mCachedAppOptimizer.freezeAppAsyncLSP(app);
+        } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ && opt.isFrozen()) {
+            mCachedAppOptimizer.unfreezeAppLSP(app);
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/PackageList.java b/services/core/java/com/android/server/am/PackageList.java
index 978bcb7..aa10d52 100644
--- a/services/core/java/com/android/server/am/PackageList.java
+++ b/services/core/java/com/android/server/am/PackageList.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.annotation.NonNull;
 import android.content.pm.VersionedPackage;
 import android.util.ArrayMap;
 
@@ -73,7 +74,7 @@
         }
     }
 
-    void forEachPackage(Consumer<String> callback) {
+    void forEachPackage(@NonNull Consumer<String> callback) {
         synchronized (this) {
             for (int i = 0, size = mPkgList.size(); i < size; i++) {
                 callback.accept(mPkgList.keyAt(i));
@@ -81,7 +82,7 @@
         }
     }
 
-    void forEachPackage(BiConsumer<String, ProcessStats.ProcessStateHolder> callback) {
+    void forEachPackage(@NonNull BiConsumer<String, ProcessStats.ProcessStateHolder> callback) {
         synchronized (this) {
             for (int i = 0, size = mPkgList.size(); i < size; i++) {
                 callback.accept(mPkgList.keyAt(i), mPkgList.valueAt(i));
@@ -89,7 +90,16 @@
         }
     }
 
-    <R> R forEachPackage(Function<String, R> callback) {
+    /**
+     * Search in the package list, invoke the given {@code callback} with each of the package names
+     * in that list; if the callback returns a non-null object, halt the search, return that
+     * object as the return value of this search function.
+     *
+     * @param callback The callback interface to accept the current package name; if it returns
+     *                 a non-null object, the search will be halted and this object will be used
+     *                 as the return value of this search function.
+     */
+    <R> R searchEachPackage(@NonNull Function<String, R> callback) {
         synchronized (this) {
             for (int i = 0, size = mPkgList.size(); i < size; i++) {
                 R r = callback.apply(mPkgList.keyAt(i));
@@ -101,7 +111,7 @@
         return null;
     }
 
-    void forEachPackageProcessStats(Consumer<ProcessStats.ProcessStateHolder> callback) {
+    void forEachPackageProcessStats(@NonNull Consumer<ProcessStats.ProcessStateHolder> callback) {
         synchronized (this) {
             for (int i = 0, size = mPkgList.size(); i < size; i++) {
                 callback.accept(mPkgList.valueAt(i));
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 631b632..0eb48f6 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -24,7 +24,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.BroadcastOptions;
 import android.app.PendingIntent;
 import android.content.IIntentReceiver;
 import android.content.IIntentSender;
@@ -65,7 +64,8 @@
     boolean canceled = false;
     /**
      * Map IBinder to duration specified as Pair<Long, Integer>, Long is allowlist duration in
-     * milliseconds, Integer is allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+     * milliseconds, Integer is allowlist type defined at
+     * {@link android.os.PowerWhitelistManager.TempAllowListType}
      */
     private ArrayMap<IBinder, Pair<Long, Integer>> mWhitelistDuration;
     private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index 37b1741..4f3438fe 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -148,13 +148,14 @@
 
     @GuardedBy({"mLock", "mService.mPidsSelfLocked"})
     private void lookForPhantomProcessesLocked(ProcessRecord app) {
-        if (app.appZygote || app.killed || app.killedByAm) {
+        if (app.appZygote || app.isKilled() || app.isKilledByAm()) {
             // process forked from app zygote doesn't have its own acct entry
             return;
         }
-        InputStream input = mCgroupProcsFds.get(app.pid);
+        final int appPid = app.getPid();
+        InputStream input = mCgroupProcsFds.get(appPid);
         if (input == null) {
-            final String path = getCgroupFilePath(app.info.uid, app.pid);
+            final String path = getCgroupFilePath(app.info.uid, appPid);
             try {
                 input = mInjector.openCgroupProcs(path);
             } catch (FileNotFoundException | SecurityException e) {
@@ -164,7 +165,7 @@
                 return;
             }
             // Keep the FD open for better performance
-            mCgroupProcsFds.put(app.pid, input);
+            mCgroupProcsFds.put(appPid, input);
         }
         final byte[] buf = mDataBuffer;
         try {
@@ -180,7 +181,7 @@
                 for (int i = 0; i < read; i++) {
                     final byte b = buf[i];
                     if (b == '\n') {
-                        addChildPidLocked(app, pid);
+                        addChildPidLocked(app, pid, appPid);
                         pid = 0;
                     } else {
                         pid = pid * 10 + (b - '0');
@@ -193,14 +194,14 @@
                 }
             } while (true);
             if (pid != 0) {
-                addChildPidLocked(app, pid);
+                addChildPidLocked(app, pid, appPid);
             }
             // rewind the fd for the next read
             input.skip(-totalRead);
         } catch (IOException e) {
             Slog.e(TAG, "Error in reading cgroup procs from " + app, e);
             IoUtils.closeQuietly(input);
-            mCgroupProcsFds.delete(app.pid);
+            mCgroupProcsFds.delete(appPid);
         }
     }
 
@@ -232,8 +233,8 @@
     }
 
     @GuardedBy({"mLock", "mService.mPidsSelfLocked"})
-    private void addChildPidLocked(final ProcessRecord app, final int pid) {
-        if (app.pid != pid) {
+    private void addChildPidLocked(final ProcessRecord app, final int pid, final int appPid) {
+        if (appPid != pid) {
             // That's something else...
             final ProcessRecord r = mService.mPidsSelfLocked.get(pid);
             if (r != null) {
@@ -328,15 +329,16 @@
         if (r != null) {
             // It's a phantom process, bookkeep it
             try {
+                final int appPid = r.getPid();
                 final PhantomProcessRecord proc = new PhantomProcessRecord(
-                        processName, uid, pid, r.pid, mService,
+                        processName, uid, pid, appPid, mService,
                         this::onPhantomProcessKilledLocked);
                 proc.mUpdateSeq = mUpdateSeq;
                 mPhantomProcesses.put(pid, proc);
-                SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(r.pid);
+                SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(appPid);
                 if (array == null) {
                     array = new SparseArray<>();
-                    mAppPhantomProcessMap.put(r.pid, array);
+                    mAppPhantomProcessMap.put(appPid, array);
                 }
                 array.put(pid, proc);
                 if (proc.mPidFd != null) {
@@ -414,40 +416,42 @@
      * order of the oom adjs of their parent process.
      */
     void trimPhantomProcessesIfNecessary() {
-        synchronized (mLock) {
-            mTrimPhantomProcessScheduled = false;
-            if (mService.mConstants.MAX_PHANTOM_PROCESSES < mPhantomProcesses.size()) {
-                for (int i = mPhantomProcesses.size() - 1; i >= 0; i--) {
-                    mTempPhantomProcesses.add(mPhantomProcesses.valueAt(i));
+        synchronized (mService.mProcLock) {
+            synchronized (mLock) {
+                mTrimPhantomProcessScheduled = false;
+                if (mService.mConstants.MAX_PHANTOM_PROCESSES < mPhantomProcesses.size()) {
+                    for (int i = mPhantomProcesses.size() - 1; i >= 0; i--) {
+                        mTempPhantomProcesses.add(mPhantomProcesses.valueAt(i));
+                    }
+                    synchronized (mService.mPidsSelfLocked) {
+                        Collections.sort(mTempPhantomProcesses, (a, b) -> {
+                            final ProcessRecord ra = mService.mPidsSelfLocked.get(a.mPpid);
+                            if (ra == null) {
+                                // parent is gone, this process should have been killed too
+                                return 1;
+                            }
+                            final ProcessRecord rb = mService.mPidsSelfLocked.get(b.mPpid);
+                            if (rb == null) {
+                                // parent is gone, this process should have been killed too
+                                return -1;
+                            }
+                            if (ra.mState.getCurAdj() != rb.mState.getCurAdj()) {
+                                return ra.mState.getCurAdj() - rb.mState.getCurAdj();
+                            }
+                            if (a.mKnownSince != b.mKnownSince) {
+                                // In case of identical oom adj, younger one first
+                                return a.mKnownSince < b.mKnownSince ? 1 : -1;
+                            }
+                            return 0;
+                        });
+                    }
+                    for (int i = mTempPhantomProcesses.size() - 1;
+                            i >= mService.mConstants.MAX_PHANTOM_PROCESSES; i--) {
+                        final PhantomProcessRecord proc = mTempPhantomProcesses.get(i);
+                        proc.killLocked("Trimming phantom processes", true);
+                    }
+                    mTempPhantomProcesses.clear();
                 }
-                synchronized (mService.mPidsSelfLocked) {
-                    Collections.sort(mTempPhantomProcesses, (a, b) -> {
-                        final ProcessRecord ra = mService.mPidsSelfLocked.get(a.mPpid);
-                        if (ra == null) {
-                            // parent is gone, this process should have been killed too
-                            return 1;
-                        }
-                        final ProcessRecord rb = mService.mPidsSelfLocked.get(b.mPpid);
-                        if (rb == null) {
-                            // parent is gone, this process should have been killed too
-                            return -1;
-                        }
-                        if (ra.curAdj != rb.curAdj) {
-                            return ra.curAdj - rb.curAdj;
-                        }
-                        if (a.mKnownSince != b.mKnownSince) {
-                            // In case of identical oom adj, younger one first
-                            return a.mKnownSince < b.mKnownSince ? 1 : -1;
-                        }
-                        return 0;
-                    });
-                }
-                for (int i = mTempPhantomProcesses.size() - 1;
-                        i >= mService.mConstants.MAX_PHANTOM_PROCESSES; i--) {
-                    final PhantomProcessRecord proc = mTempPhantomProcesses.get(i);
-                    proc.killLocked("Trimming phantom processes", true);
-                }
-                mTempPhantomProcesses.clear();
             }
         }
     }
@@ -498,7 +502,7 @@
             }
         }
         // Lastly, kill the parent process too
-        app.kill("Caused by child process: " + msg, reasonCode, subReason, true);
+        app.killLocked("Caused by child process: " + msg, reasonCode, subReason, true);
     }
 
     /**
@@ -508,7 +512,7 @@
     void forEachPhantomProcessOfApp(final ProcessRecord app,
             final Function<PhantomProcessRecord, Boolean> callback) {
         synchronized (mLock) {
-            int index = mAppPhantomProcessMap.indexOfKey(app.pid);
+            int index = mAppPhantomProcessMap.indexOfKey(app.getPid());
             if (index >= 0) {
                 final SparseArray<PhantomProcessRecord> array =
                         mAppPhantomProcessMap.valueAt(index);
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
new file mode 100644
index 0000000..4643610
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 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 com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+
+/**
+ * The state info of app when it's cached, used by the optimizer.
+ */
+final class ProcessCachedOptimizerRecord {
+    private final ProcessRecord mApp;
+
+    private final ActivityManagerGlobalLock mProcLock;
+
+    /**
+     * The last time that this process was compacted.
+     */
+    @GuardedBy("mProcLock")
+    private long mLastCompactTime;
+
+    /**
+     * The most recent compaction action requested for this app.
+     */
+    @GuardedBy("mProcLock")
+    private int mReqCompactAction;
+
+    /**
+     * The most recent compaction action performed for this app.
+     */
+    @GuardedBy("mProcLock")
+    private int mLastCompactAction;
+
+    /**
+     * True when the process is frozen.
+     */
+    @GuardedBy("mProcLock")
+    private boolean mFrozen;
+
+    /**
+     * An override on the freeze state is in progress.
+     */
+    @GuardedBy("mProcLock")
+    boolean mFreezerOverride;
+
+    /**
+     * Last time the app was (un)frozen, 0 for never.
+     */
+    @GuardedBy("mProcLock")
+    private long mFreezeUnfreezeTime;
+
+    /**
+     * True if a process has a WPRI binding from an unfrozen process.
+     */
+    @GuardedBy("mProcLock")
+    private boolean mShouldNotFreeze;
+
+    @GuardedBy("mProcLock")
+    long getLastCompactTime() {
+        return mLastCompactTime;
+    }
+
+    @GuardedBy("mProcLock")
+    void setLastCompactTime(long lastCompactTime) {
+        mLastCompactTime = lastCompactTime;
+    }
+
+    @GuardedBy("mProcLock")
+    int getReqCompactAction() {
+        return mReqCompactAction;
+    }
+
+    @GuardedBy("mProcLock")
+    void setReqCompactAction(int reqCompactAction) {
+        mReqCompactAction = reqCompactAction;
+    }
+
+    @GuardedBy("mProcLock")
+    int getLastCompactAction() {
+        return mLastCompactAction;
+    }
+
+    @GuardedBy("mProcLock")
+    void setLastCompactAction(int lastCompactAction) {
+        mLastCompactAction = lastCompactAction;
+    }
+
+    @GuardedBy("mProcLock")
+    boolean isFrozen() {
+        return mFrozen;
+    }
+
+    @GuardedBy("mProcLock")
+    void setFrozen(boolean frozen) {
+        mFrozen = frozen;
+    }
+
+    @GuardedBy("mProcLock")
+    boolean hasFreezerOverride() {
+        return mFreezerOverride;
+    }
+
+    @GuardedBy("mProcLock")
+    void setFreezerOverride(boolean freezerOverride) {
+        mFreezerOverride = freezerOverride;
+    }
+
+    @GuardedBy("mProcLock")
+    long getFreezeUnfreezeTime() {
+        return mFreezeUnfreezeTime;
+    }
+
+    @GuardedBy("mProcLock")
+    void setFreezeUnfreezeTime(long freezeUnfreezeTime) {
+        mFreezeUnfreezeTime = freezeUnfreezeTime;
+    }
+
+    @GuardedBy("mProcLock")
+    boolean shouldNotFreeze() {
+        return mShouldNotFreeze;
+    }
+
+    @GuardedBy("mProcLock")
+    void setShouldNotFreeze(boolean shouldNotFreeze) {
+        mShouldNotFreeze = shouldNotFreeze;
+    }
+
+    ProcessCachedOptimizerRecord(ProcessRecord app) {
+        mApp = app;
+        mProcLock = app.mService.mProcLock;
+    }
+
+    void init(long nowUptime) {
+        mFreezeUnfreezeTime = nowUptime;
+    }
+
+    @GuardedBy("mProcLock")
+    void dump(PrintWriter pw, String prefix, long nowUptime) {
+        pw.print(prefix); pw.print("lastCompactTime="); pw.print(mLastCompactTime);
+        pw.print(" lastCompactAction="); pw.println(mLastCompactAction);
+    }
+}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
new file mode 100644
index 0000000..64e307a
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2020 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.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
+import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.ProcessRecord.TAG;
+
+import android.app.ActivityManager;
+import android.app.ApplicationErrorReport;
+import android.app.ApplicationExitInfo;
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IncrementalStatesInfo;
+import android.content.pm.PackageManagerInternal;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.EventLog;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.CompositeRWLock;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.MemoryPressureUtil;
+import com.android.server.wm.WindowProcessController;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+
+/**
+ * The error state of the process, such as if it's crashing/ANR etc.
+ */
+class ProcessErrorStateRecord {
+    final ProcessRecord mApp;
+    private final ActivityManagerService mService;
+
+    private final ActivityManagerGlobalLock mProcLock;
+
+    /**
+     * True if disabled in the bad process list.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mBad;
+
+    /**
+     * Are we in the process of crashing?
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mCrashing;
+
+    /**
+     * Suppress normal auto-dismiss of crash dialog &amp; report UI?
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mForceCrashReport;
+
+    /**
+     * Does the app have a not responding dialog?
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mNotResponding;
+
+    /**
+     * The report about crash of the app, generated &amp; stored when an app gets into a crash.
+     * Will be "null" when all is OK.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private ActivityManager.ProcessErrorStateInfo mCrashingReport;
+
+    /**
+     * The report about ANR of the app, generated &amp; stored when an app gets into an ANR.
+     * Will be "null" when all is OK.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private ActivityManager.ProcessErrorStateInfo mNotRespondingReport;
+
+    /**
+     * Controller for error dialogs.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private final ErrorDialogController mDialogController;
+
+    /**
+     * Who will be notified of the error. This is usually an activity in the
+     * app that installed the package.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private ComponentName mErrorReportReceiver;
+
+    /**
+     * Optional local handler to be invoked in the process crash.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private Runnable mCrashHandler;
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isBad() {
+        return mBad;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setBad(boolean bad) {
+        mBad = bad;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isCrashing() {
+        return mCrashing;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setCrashing(boolean crashing) {
+        mCrashing = crashing;
+        mApp.getWindowProcessController().setCrashing(crashing);
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isForceCrashReport() {
+        return mForceCrashReport;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setForceCrashReport(boolean forceCrashReport) {
+        mForceCrashReport = forceCrashReport;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isNotResponding() {
+        return mNotResponding;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setNotResponding(boolean notResponding) {
+        mNotResponding = notResponding;
+        mApp.getWindowProcessController().setNotResponding(notResponding);
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    Runnable getCrashHandler() {
+        return mCrashHandler;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setCrashHandler(Runnable crashHandler) {
+        mCrashHandler = crashHandler;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    ActivityManager.ProcessErrorStateInfo getCrashingReport() {
+        return mCrashingReport;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setCrashingReport(ActivityManager.ProcessErrorStateInfo crashingReport) {
+        mCrashingReport = crashingReport;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    ActivityManager.ProcessErrorStateInfo getNotRespondingReport() {
+        return mNotRespondingReport;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setNotRespondingReport(ActivityManager.ProcessErrorStateInfo notRespondingReport) {
+        mNotRespondingReport = notRespondingReport;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    ComponentName getErrorReportReceiver() {
+        return mErrorReportReceiver;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setErrorReportReceiver(ComponentName errorReportReceiver) {
+        mErrorReportReceiver = errorReportReceiver;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    ErrorDialogController getDialogController() {
+        return mDialogController;
+    }
+
+    ProcessErrorStateRecord(ProcessRecord app) {
+        mApp = app;
+        mService = app.mService;
+        mProcLock = mService.mProcLock;
+        mDialogController = new ErrorDialogController(app);
+    }
+
+    void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
+            String parentShortComponentName, WindowProcessController parentProcess,
+            boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
+        ArrayList<Integer> firstPids = new ArrayList<>(5);
+        SparseArray<Boolean> lastPids = new SparseArray<>(20);
+
+        mApp.getWindowProcessController().appEarlyNotResponding(annotation, () -> {
+            synchronized (mService) {
+                mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);
+            }
+        });
+
+        long anrTime = SystemClock.uptimeMillis();
+        if (isMonitorCpuUsage()) {
+            mService.updateCpuStatsNow();
+        }
+
+        final boolean isSilentAnr;
+        final int pid = mApp.getPid();
+        synchronized (mService) {
+            // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
+            if (mService.mAtmInternal.isShuttingDown()) {
+                Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
+                return;
+            } else if (isNotResponding()) {
+                Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
+                return;
+            } else if (isCrashing()) {
+                Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
+                return;
+            } else if (mApp.isKilledByAm()) {
+                Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
+                return;
+            } else if (mApp.isKilled()) {
+                Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
+                return;
+            }
+
+            // In case we come through here for the same app before completing
+            // this one, mark as anring now so we will bail out.
+            synchronized (mProcLock) {
+                setNotResponding(true);
+            }
+
+            // Log the ANR to the event log.
+            EventLog.writeEvent(EventLogTags.AM_ANR, mApp.userId, pid, mApp.processName,
+                    mApp.info.flags, annotation);
+
+            // Dump thread traces as quickly as we can, starting with "interesting" processes.
+            firstPids.add(pid);
+
+            // Don't dump other PIDs if it's a background ANR or is requested to only dump self.
+            isSilentAnr = isSilentAnr();
+            if (!isSilentAnr && !onlyDumpSelf) {
+                int parentPid = pid;
+                if (parentProcess != null && parentProcess.getPid() > 0) {
+                    parentPid = parentProcess.getPid();
+                }
+                if (parentPid != pid) firstPids.add(parentPid);
+
+                if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
+
+                final int ppid = parentPid;
+                mService.mProcessList.forEachLruProcessesLOSP(false, r -> {
+                    if (r != null && r.getThread() != null) {
+                        int myPid = r.getPid();
+                        if (myPid > 0 && myPid != pid && myPid != ppid && myPid != MY_PID) {
+                            if (r.isPersistent()) {
+                                firstPids.add(myPid);
+                                if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
+                            } else if (r.mServices.isTreatedLikeActivity()) {
+                                firstPids.add(myPid);
+                                if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
+                            } else {
+                                lastPids.put(myPid, Boolean.TRUE);
+                                if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
+                            }
+                        }
+                    }
+                });
+            }
+        }
+
+        // Check if package is still being loaded
+        boolean isPackageLoading = false;
+        final PackageManagerInternal packageManagerInternal = mService.getPackageManagerInternal();
+        if (aInfo != null && aInfo.packageName != null) {
+            IncrementalStatesInfo incrementalStatesInfo =
+                    packageManagerInternal.getIncrementalStatesInfo(
+                            aInfo.packageName, mApp.uid, mApp.userId);
+            if (incrementalStatesInfo != null) {
+                isPackageLoading = incrementalStatesInfo.isLoading();
+            }
+        }
+
+        // Log the ANR to the main log.
+        StringBuilder info = new StringBuilder();
+        info.setLength(0);
+        info.append("ANR in ").append(mApp.processName);
+        if (activityShortComponentName != null) {
+            info.append(" (").append(activityShortComponentName).append(")");
+        }
+        info.append("\n");
+        info.append("PID: ").append(pid).append("\n");
+        if (annotation != null) {
+            info.append("Reason: ").append(annotation).append("\n");
+        }
+        if (parentShortComponentName != null
+                && parentShortComponentName.equals(activityShortComponentName)) {
+            info.append("Parent: ").append(parentShortComponentName).append("\n");
+        }
+
+        if (isPackageLoading) {
+            // Report in the main log that the package is still loading
+            final float loadingProgress = packageManagerInternal.getIncrementalStatesInfo(
+                    aInfo.packageName, mApp.uid, mApp.userId).getProgress();
+            info.append("Package is ").append((int) (loadingProgress * 100)).append("% loaded.\n");
+        }
+
+        StringBuilder report = new StringBuilder();
+        report.append(MemoryPressureUtil.currentPsiState());
+        ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
+
+        // don't dump native PIDs for background ANRs unless it is the process of interest
+        String[] nativeProcs = null;
+        if (isSilentAnr || onlyDumpSelf) {
+            for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
+                if (NATIVE_STACKS_OF_INTEREST[i].equals(mApp.processName)) {
+                    nativeProcs = new String[] { mApp.processName };
+                    break;
+                }
+            }
+        } else {
+            nativeProcs = NATIVE_STACKS_OF_INTEREST;
+        }
+
+        int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);
+        ArrayList<Integer> nativePids = null;
+
+        if (pids != null) {
+            nativePids = new ArrayList<>(pids.length);
+            for (int i : pids) {
+                nativePids.add(i);
+            }
+        }
+
+        // For background ANRs, don't pass the ProcessCpuTracker to
+        // avoid spending 1/2 second collecting stats to rank lastPids.
+        StringWriter tracesFileException = new StringWriter();
+        // To hold the start and end offset to the ANR trace file respectively.
+        final long[] offsets = new long[2];
+        File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
+                isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
+                nativePids, tracesFileException, offsets);
+
+        if (isMonitorCpuUsage()) {
+            mService.updateCpuStatsNow();
+            mService.mAppProfiler.printCurrentCpuState(report, anrTime);
+            info.append(processCpuTracker.printCurrentLoad());
+            info.append(report);
+        }
+        report.append(tracesFileException.getBuffer());
+
+        info.append(processCpuTracker.printCurrentState(anrTime));
+
+        Slog.e(TAG, info.toString());
+        if (tracesFile == null) {
+            // There is no trace file, so dump (only) the alleged culprit's threads to the log
+            Process.sendSignal(pid, Process.SIGNAL_QUIT);
+        } else if (offsets[1] > 0) {
+            // We've dumped into the trace file successfully
+            mService.mProcessList.mAppExitInfoTracker.scheduleLogAnrTrace(
+                    pid, mApp.uid, mApp.getPackageList(), tracesFile, offsets[0], offsets[1]);
+        }
+
+        FrameworkStatsLog.write(FrameworkStatsLog.ANR_OCCURRED, mApp.uid, mApp.processName,
+                activityShortComponentName == null ? "unknown" : activityShortComponentName,
+                annotation,
+                (mApp.info != null) ? (mApp.info.isInstantApp()
+                        ? FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE
+                        : FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE)
+                        : FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE,
+                mApp.isInterestingToUserLocked()
+                        ? FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
+                        : FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND,
+                mApp.getProcessClassEnum(),
+                (mApp.info != null) ? mApp.info.packageName : "", isPackageLoading);
+        final ProcessRecord parentPr = parentProcess != null
+                ? (ProcessRecord) parentProcess.mOwner : null;
+        mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
+                parentShortComponentName, parentPr, annotation, report.toString(), tracesFile,
+                null);
+
+        if (mApp.getWindowProcessController().appNotResponding(info.toString(),
+                () -> {
+                    synchronized (mService) {
+                        mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);
+                    }
+                },
+                () -> {
+                    synchronized (mService) {
+                        mService.mServices.scheduleServiceTimeoutLocked(mApp);
+                    }
+                })) {
+            return;
+        }
+
+        synchronized (mService) {
+            // mBatteryStatsService can be null if the AMS is constructed with injector only. This
+            // will only happen in tests.
+            if (mService.mBatteryStatsService != null) {
+                mService.mBatteryStatsService.noteProcessAnr(mApp.processName, mApp.uid);
+            }
+
+            if (isSilentAnr() && !mApp.isDebugging()) {
+                mApp.killLocked("bg anr", ApplicationExitInfo.REASON_ANR, true);
+                return;
+            }
+
+            synchronized (mProcLock) {
+                // Set the app's notResponding state, and look up the errorReportReceiver
+                makeAppNotRespondingLSP(activityShortComponentName,
+                        annotation != null ? "ANR " + annotation : "ANR", info.toString());
+            }
+
+            // Notify package manager service to possibly update package state
+            if (aInfo != null && aInfo.packageName != null) {
+                packageManagerInternal.notifyPackageCrashOrAnr(aInfo.packageName);
+            }
+
+            // mUiHandler can be null if the AMS is constructed with injector only. This will only
+            // happen in tests.
+            if (mService.mUiHandler != null) {
+                // Bring up the infamous App Not Responding dialog
+                Message msg = Message.obtain();
+                msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
+                msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem);
+
+                mService.mUiHandler.sendMessage(msg);
+            }
+        }
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    private void makeAppNotRespondingLSP(String activity, String shortMsg, String longMsg) {
+        setNotResponding(true);
+        // mAppErrors can be null if the AMS is constructed with injector only. This will only
+        // happen in tests.
+        if (mService.mAppErrors != null) {
+            mNotRespondingReport = mService.mAppErrors.generateProcessError(mApp,
+                    ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
+                    activity, shortMsg, longMsg, null);
+        }
+        startAppProblemLSP();
+        mApp.getWindowProcessController().stopFreezingActivities();
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void startAppProblemLSP() {
+        // If this app is not running under the current user, then we can't give it a report button
+        // because that would require launching the report UI under a different user.
+        mErrorReportReceiver = null;
+
+        for (int userId : mService.mUserController.getCurrentProfileIds()) {
+            if (mApp.userId == userId) {
+                mErrorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
+                        mService.mContext, mApp.info.packageName, mApp.info.flags);
+            }
+        }
+        mService.skipCurrentReceiverLocked(mApp);
+    }
+
+    @GuardedBy("mService")
+    private boolean isInterestingForBackgroundTraces() {
+        // The system_server is always considered interesting.
+        if (mApp.getPid() == MY_PID) {
+            return true;
+        }
+
+        // A package is considered interesting if any of the following is true :
+        //
+        // - It's displaying an activity.
+        // - It's the SystemUI.
+        // - It has an overlay or a top UI visible.
+        //
+        // NOTE: The check whether a given ProcessRecord belongs to the systemui
+        // process is a bit of a kludge, but the same pattern seems repeated at
+        // several places in the system server.
+        return mApp.isInterestingToUserLocked()
+                || (mApp.info != null && "com.android.systemui".equals(mApp.info.packageName))
+                || (mApp.mState.hasTopUi() || mApp.mState.hasOverlayUi());
+    }
+
+    private boolean getShowBackground() {
+        return Settings.Secure.getInt(mService.mContext.getContentResolver(),
+                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
+    }
+
+    /**
+     * Unless configured otherwise, swallow ANRs in background processes & kill the process.
+     * Non-private access is for tests only.
+     */
+    @VisibleForTesting
+    @GuardedBy("mService")
+    boolean isSilentAnr() {
+        return !getShowBackground() && !isInterestingForBackgroundTraces();
+    }
+
+    /** Non-private access is for tests only. */
+    @VisibleForTesting
+    boolean isMonitorCpuUsage() {
+        return mService.mAppProfiler.MONITOR_CPU_USAGE;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void onCleanupApplicationRecordLSP() {
+        // Dismiss any open dialogs.
+        getDialogController().clearAllErrorDialogs();
+
+        setCrashing(false);
+        setNotResponding(false);
+    }
+
+    void dump(PrintWriter pw, String prefix, long nowUptime) {
+        synchronized (mProcLock) {
+            if (mCrashing || mDialogController.hasCrashDialogs() || mNotResponding
+                    || mDialogController.hasAnrDialogs() || mBad) {
+                pw.print(prefix);
+                pw.print(" mCrashing=" + mCrashing);
+                pw.print(" " + mDialogController.getCrashDialogs());
+                pw.print(" mNotResponding=" + mNotResponding);
+                pw.print(" " + mDialogController.getAnrDialogs());
+                pw.print(" bad=" + mBad);
+
+                // mCrashing or mNotResponding is always set before errorReportReceiver
+                if (mErrorReportReceiver != null) {
+                    pw.print(" errorReportReceiver=");
+                    pw.print(mErrorReportReceiver.flattenToShortString());
+                }
+                pw.println();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 8f99459..0576345 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -55,6 +55,7 @@
 import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
 import static com.android.server.am.AppProfiler.TAG_PSS;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
@@ -114,6 +115,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 
+import com.android.internal.annotations.CompositeRWLock;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ProcessMap;
@@ -149,6 +151,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * Activity manager code dealing with processes.
@@ -372,6 +376,13 @@
     private static final long NATIVE_MEMTAG_SYNC = 177438394; // This is a bug id.
 
     /**
+     * Enable automatic zero-initialization of native heap memory allocations.
+     */
+    @ChangeId
+    @Disabled
+    private static final long NATIVE_HEAP_ZERO_INIT = 178038272; // This is a bug id.
+
+    /**
      * Enable sampled memory bug detection in the app.
      * @see <a href="https://source.android.com/devices/tech/debug/gwp-asan">GWP-ASan</a>.
      */
@@ -466,34 +477,41 @@
      * List of running applications, sorted by recent usage.
      * The first entry in the list is the least recently used.
      */
-    final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
+    @CompositeRWLock({"mService", "mProcLock"})
+    private final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
 
     /**
      * Where in mLruProcesses that the processes hosting activities start.
      */
-    int mLruProcessActivityStart = 0;
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mLruProcessActivityStart = 0;
 
     /**
      * Where in mLruProcesses that the processes hosting services start.
      * This is after (lower index) than mLruProcessesActivityStart.
      */
-    int mLruProcessServiceStart = 0;
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mLruProcessServiceStart = 0;
 
     /**
      * Current sequence id for process LRU updating.
      */
-    int mLruSeq = 0;
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mLruSeq = 0;
 
+    @CompositeRWLock({"mService", "mProcLock"})
     ActiveUids mActiveUids;
 
     /**
      * The currently running isolated processes.
      */
+    @GuardedBy("mService")
     final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
 
     /**
      * The currently running application zygotes.
      */
+    @GuardedBy("mService")
     final ProcessMap<AppZygote> mAppZygotes = new ProcessMap<AppZygote>();
 
     /**
@@ -505,6 +523,7 @@
     /**
      * The processes that are forked off an application zygote.
      */
+    @GuardedBy("mService")
     final ArrayMap<AppZygote, ArrayList<ProcessRecord>> mAppZygoteProcesses =
             new ArrayMap<AppZygote, ArrayList<ProcessRecord>>();
 
@@ -532,6 +551,8 @@
      */
     private final int[] mZygoteSigChldMessage = new int[3];
 
+    ActivityManagerGlobalLock mProcLock;
+
     final class IsolatedUidRange {
         @VisibleForTesting
         public final int mFirstUid;
@@ -640,6 +661,7 @@
      * The available isolated UIDs for processes that are not spawned from an application zygote.
      */
     @VisibleForTesting
+    @GuardedBy("mService")
     IsolatedUidRange mGlobalIsolatedUids = new IsolatedUidRange(Process.FIRST_ISOLATED_UID,
             Process.LAST_ISOLATED_UID);
 
@@ -647,6 +669,7 @@
      * An allocator for isolated UID ranges for apps that use an application zygote.
      */
     @VisibleForTesting
+    @GuardedBy("mService")
     IsolatedUidRangeAllocator mAppIsolatedUidRangeAllocator =
             new IsolatedUidRangeAllocator(Process.FIRST_APP_ZYGOTE_ISOLATED_UID,
                     Process.LAST_APP_ZYGOTE_ISOLATED_UID, Process.NUM_UIDS_PER_APP_ZYGOTE);
@@ -654,6 +677,7 @@
     /**
      * Processes that are being forcibly torn down.
      */
+    @GuardedBy("mService")
     final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
 
     // Self locked with the inner lock within the RemoteCallbackList
@@ -674,14 +698,14 @@
      */
     private final Object mProcessChangeLock = new Object();
 
-
     /**
      * All of the applications we currently have running organized by name.
      * The keys are strings of the application package name (as
      * returned by the package manager), and the keys are ApplicationRecord
      * objects.
      */
-    final MyProcessMap mProcessNames = new MyProcessMap();
+    @CompositeRWLock({"mService", "mProcLock"})
+    private final MyProcessMap mProcessNames = new MyProcessMap();
 
     final class MyProcessMap extends ProcessMap<ProcessRecord> {
         @Override
@@ -749,6 +773,7 @@
         mService = service;
         mActiveUids = activeUids;
         mPlatformCompat = platformCompat;
+        mProcLock = service.mProcLock;
         // Get this after boot, and won't be changed until it's rebooted, as we don't
         // want some apps enabled while some apps disabled
         mAppDataIsolationEnabled =
@@ -842,13 +867,13 @@
      */
     Map<Integer, String> getProcessesWithPendingBindMounts(int userId) {
         final Map<Integer, String> pidPackageMap = new HashMap<>();
-        synchronized (mService) {
+        synchronized (mProcLock) {
             for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
                 final ProcessRecord record = mLruProcesses.get(i);
-                if (record.userId != userId || !record.bindMountPending) {
+                if (record.userId != userId || !record.isBindMountPending()) {
                     continue;
                 }
-                final int pid = record.pid;
+                final int pid = record.getPid();
                 // It can happen when app process is starting, but zygote work is not done yet so
                 // system does not this pid record yet.
                 if (pid == 0) {
@@ -857,8 +882,8 @@
                 }
                 pidPackageMap.put(pid, record.info.packageName);
             }
-            return pidPackageMap;
         }
+        return pidPackageMap;
     }
 
     private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
@@ -1534,6 +1559,7 @@
         }
     }
 
+    @GuardedBy("mService")
     final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean
             keepIfLarge) {
         if (uid == SYSTEM_UID) {
@@ -1554,21 +1580,19 @@
         }
         ProcessRecord proc = mProcessNames.get(processName, uid);
         if (false && proc != null && !keepIfLarge
-                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
+                && proc.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
                 && proc.mProfile.getLastCachedPss() >= 4000) {
             // Turn this condition on to cause killing to happen regularly, for testing.
-            final long lastCachedPss;
             synchronized (mService.mAppProfiler.mProfilerLock) {
                 proc.mProfile.reportCachedKill();
-                lastCachedPss = proc.mProfile.getLastCachedPss();
             }
-            proc.kill(Long.toString(lastCachedPss) + "k from cached",
+            proc.killLocked(Long.toString(proc.mProfile.getLastCachedPss()) + "k from cached",
                     ApplicationExitInfo.REASON_OTHER,
                     ApplicationExitInfo.SUBREASON_LARGE_CACHED,
                     true);
         } else if (proc != null && !keepIfLarge
                 && !mService.mAppProfiler.isLastMemoryLevelNormal()
-                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+                && proc.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
             final long lastCachedPss;
             boolean doKilling = false;
             synchronized (mService.mAppProfiler.mProfilerLock) {
@@ -1582,7 +1606,7 @@
                 Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + lastCachedPss);
             }
             if (doKilling) {
-                proc.kill(Long.toString(lastCachedPss) + "k from cached",
+                proc.killLocked(Long.toString(lastCachedPss) + "k from cached",
                         ApplicationExitInfo.REASON_OTHER,
                         ApplicationExitInfo.SUBREASON_LARGE_CACHED,
                         true);
@@ -1604,14 +1628,16 @@
         outInfo.foregroundAppThreshold = getMemLevel(FOREGROUND_APP_ADJ);
     }
 
-    ProcessRecord findAppProcessLocked(IBinder app, String reason) {
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    ProcessRecord findAppProcessLOSP(IBinder app, String reason) {
         final int NP = mProcessNames.getMap().size();
         for (int ip = 0; ip < NP; ip++) {
             SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
             final int NA = apps.size();
             for (int ia = 0; ia < NA; ia++) {
                 ProcessRecord p = apps.valueAt(ia);
-                if (p.thread != null && p.thread.asBinder() == app) {
+                final IApplicationThread thread = p.getThread();
+                if (thread != null && thread.asBinder() == app) {
                     return p;
                 }
             }
@@ -1685,12 +1711,28 @@
         return gidArray;
     }
 
-    // Returns the memory tagging level to be enabled. If memory tagging isn't
-    // requested, returns zero.
-    private int getMemtagLevel(ProcessRecord app) {
-        // Ensure the hardware + kernel actually supports MTE.
-        if (!Zygote.nativeSupportsMemoryTagging()) {
-            return 0;
+    private int memtagModeToZygoteMemtagLevel(int memtagMode) {
+        switch (memtagMode) {
+            case ApplicationInfo.MEMTAG_ASYNC:
+                return Zygote.MEMORY_TAG_LEVEL_ASYNC;
+            case ApplicationInfo.MEMTAG_SYNC:
+                return Zygote.MEMORY_TAG_LEVEL_SYNC;
+            default:
+                return Zygote.MEMORY_TAG_LEVEL_NONE;
+        }
+    }
+
+    // Returns the requested memory tagging level.
+    private int getRequestedMemtagLevel(ProcessRecord app) {
+        // Look at the process attribute first.
+        if (app.processInfo != null
+                && app.processInfo.memtagMode != ApplicationInfo.MEMTAG_DEFAULT) {
+            return memtagModeToZygoteMemtagLevel(app.processInfo.memtagMode);
+        }
+
+        // Then at the application attribute.
+        if (app.info.getMemtagMode() != ApplicationInfo.MEMTAG_DEFAULT) {
+            return memtagModeToZygoteMemtagLevel(app.info.getMemtagMode());
         }
 
         if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_SYNC, app.info)) {
@@ -1701,40 +1743,43 @@
             return Zygote.MEMORY_TAG_LEVEL_ASYNC;
         }
 
-        return 0;
-    }
-
-    private boolean shouldEnableTaggedPointers(ProcessRecord app) {
-        // Ensure we have platform + kernel support for TBI.
-        if (!Zygote.nativeSupportsTaggedPointers()) {
-            return false;
-        }
-
         // Check to ensure the app hasn't explicitly opted-out of TBI via. the manifest attribute.
         if (!app.info.allowsNativeHeapPointerTagging()) {
-            return false;
+            return Zygote.MEMORY_TAG_LEVEL_NONE;
         }
 
         // Check to see that the compat feature for TBI is enabled.
-        if (!mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    private int decideTaggingLevel(ProcessRecord app) {
-        // Check MTE support first, as it should take precedence over TBI.
-        int memtagLevel = getMemtagLevel(app);
-        if (memtagLevel != 0) {
-            return memtagLevel;
-        }
-
-        if (shouldEnableTaggedPointers(app)) {
+        if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
             return Zygote.MEMORY_TAG_LEVEL_TBI;
         }
 
-        return 0;
+        return Zygote.MEMORY_TAG_LEVEL_NONE;
+    }
+
+    private int decideTaggingLevel(ProcessRecord app) {
+        // Get the desired tagging level (app manifest + compat features).
+        int level = getRequestedMemtagLevel(app);
+
+        // Take into account the hardware capabilities.
+        if (Zygote.nativeSupportsMemoryTagging()) {
+            // MTE devices can not do TBI, because the Zygote process already has live MTE
+            // allocations. Downgrade TBI to NONE.
+            if (level == Zygote.MEMORY_TAG_LEVEL_TBI) {
+                level = Zygote.MEMORY_TAG_LEVEL_NONE;
+            }
+        } else if (Zygote.nativeSupportsTaggedPointers()) {
+            // TBI-but-not-MTE devices downgrade MTE modes to TBI.
+            // The idea is that if an app opts into full hardware tagging (MTE), it must be ok with
+            // the "fake" pointer tagging (TBI).
+            if (level == Zygote.MEMORY_TAG_LEVEL_ASYNC || level == Zygote.MEMORY_TAG_LEVEL_SYNC) {
+                level = Zygote.MEMORY_TAG_LEVEL_TBI;
+            }
+        } else {
+            // Otherwise disable all tagging.
+            level = Zygote.MEMORY_TAG_LEVEL_NONE;
+        }
+
+        return level;
     }
 
     private int decideGwpAsanLevel(ProcessRecord app) {
@@ -1745,7 +1790,7 @@
                     ? Zygote.GWP_ASAN_LEVEL_ALWAYS
                     : Zygote.GWP_ASAN_LEVEL_NEVER;
         }
-        // Then at the applicaton attribute.
+        // Then at the application attribute.
         if (app.info.getGwpAsanMode() != ApplicationInfo.GWP_ASAN_DEFAULT) {
             return app.info.getGwpAsanMode() == ApplicationInfo.GWP_ASAN_ALWAYS
                     ? Zygote.GWP_ASAN_LEVEL_ALWAYS
@@ -1762,24 +1807,40 @@
         return Zygote.GWP_ASAN_LEVEL_NEVER;
     }
 
+    private boolean enableNativeHeapZeroInit(ProcessRecord app) {
+        // Look at the process attribute first.
+        if (app.processInfo != null && app.processInfo.nativeHeapZeroInit != null) {
+            return app.processInfo.nativeHeapZeroInit;
+        }
+        // Then at the application attribute.
+        if (app.info.isNativeHeapZeroInit() != null) {
+            return app.info.isNativeHeapZeroInit();
+        }
+        // Compat feature last.
+        if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_ZERO_INIT, app.info)) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * @return {@code true} if process start is successful, false otherwise.
      */
     @GuardedBy("mService")
     boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
             int zygotePolicyFlags, boolean disableHiddenApiChecks, String abiOverride) {
-        if (app.pendingStart) {
+        if (app.isPendingStart()) {
             return true;
         }
         long startTime = SystemClock.uptimeMillis();
-        if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
+        if (app.getPid() > 0 && app.getPid() != ActivityManagerService.MY_PID) {
             checkSlow(startTime, "startProcess: removing from pids map");
-            mService.removePidLocked(app);
-            app.bindMountPending = false;
+            mService.removePidLocked(app.getPid(), app);
+            app.setBindMountPending(false);
             mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
             checkSlow(startTime, "startProcess: done removing from pids map");
             app.setPid(0);
-            app.startSeq = 0;
+            app.setStartSeq(0);
         }
 
         if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v(
@@ -1834,7 +1895,7 @@
 
                 gids = computeGidsForProcess(mountExternal, uid, permGids);
             }
-            app.mountMode = mountExternal;
+            app.setMountMode(mountExternal);
             checkSlow(startTime, "startProcess: building args");
             if (mService.mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
                 uid = 0;
@@ -1950,9 +2011,9 @@
                 instructionSet = VMRuntime.getInstructionSet(requiredAbi);
             }
 
-            app.gids = gids;
+            app.setGids(gids);
             app.setRequiredAbi(requiredAbi);
-            app.instructionSet = instructionSet;
+            app.setInstructionSet(instructionSet);
 
             // If instructionSet is non-null, this indicates that the system_server is spawning a
             // process with an ISA that may be different from its own. System (kernel and hardware)
@@ -1968,6 +2029,10 @@
                 runtimeFlags |= decideTaggingLevel(app);
             }
 
+            if (enableNativeHeapZeroInit(app)) {
+                runtimeFlags |= Zygote.NATIVE_HEAP_ZERO_INIT;
+            }
+
             // the per-user SELinux context must be set
             if (TextUtils.isEmpty(app.info.seInfoUser)) {
                 Slog.wtf(ActivityManagerService.TAG, "SELinux tag not defined",
@@ -2003,23 +2068,26 @@
             int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
             String seInfo, String requiredAbi, String instructionSet, String invokeWith,
             long startTime) {
-        app.pendingStart = true;
-        app.killedByAm = false;
-        app.removed = false;
-        app.killed = false;
-        if (app.startSeq != 0) {
-            Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
-                    + " with non-zero startSeq:" + app.startSeq);
+        app.setPendingStart(true);
+        app.setRemoved(false);
+        synchronized (mProcLock) {
+            app.setKilledByAm(false);
+            app.setKilled(false);
         }
-        if (app.pid != 0) {
+        if (app.getStartSeq() != 0) {
             Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
-                    + " with non-zero pid:" + app.pid);
+                    + " with non-zero startSeq:" + app.getStartSeq());
         }
-        app.mDisabledCompatChanges = null;
+        if (app.getPid() != 0) {
+            Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+                    + " with non-zero pid:" + app.getPid());
+        }
+        app.setDisabledCompatChanges(null);
         if (mPlatformCompat != null) {
-            app.mDisabledCompatChanges = mPlatformCompat.getDisabledChanges(app.info);
+            app.setDisabledCompatChanges(mPlatformCompat.getDisabledChanges(app.info));
         }
-        final long startSeq = app.startSeq = ++mProcStartSeqCounter;
+        final long startSeq = ++mProcStartSeqCounter;
+        app.setStartSeq(startSeq);
         app.setStartParams(uid, hostingRecord, seInfo, startTime);
         app.setUsingWrapper(invokeWith != null
                 || Zygote.getWrapProperty(app.processName) != null);
@@ -2043,11 +2111,11 @@
             } catch (RuntimeException e) {
                 Slog.e(ActivityManagerService.TAG, "Failure starting process "
                         + app.processName, e);
-                app.pendingStart = false;
+                app.setPendingStart(false);
                 mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
                         false, false, true, false, false, app.userId, "start failure");
             }
-            return app.pid > 0;
+            return app.getPid() > 0;
         }
     }
 
@@ -2060,11 +2128,11 @@
             final int[] gids, final int runtimeFlags, int zygotePolicyFlags,
             final int mountExternal, final String requiredAbi, final String instructionSet,
             final String invokeWith, final long startSeq) {
-        // If there is a precede instance of the process, wait for its death with a timeout.
+        // If there is a preceding instance of the process, wait for its death with a timeout.
         // Use local reference since we are not using locks here
-        final ProcessRecord precedence = app.mPrecedence;
-        if (precedence != null) {
-            final int pid = precedence.pid;
+        final ProcessRecord predecessor = app.mPredecessor;
+        if (predecessor != null) {
+            final int pid = predecessor.getPid();
             long now = System.currentTimeMillis();
             final long end = now + PROC_KILL_TIMEOUT;
             final int oldPolicy = StrictMode.getThreadPolicyMask();
@@ -2072,34 +2140,34 @@
                 StrictMode.setThreadPolicyMask(0);
                 Process.waitForProcessDeath(pid, PROC_KILL_TIMEOUT);
                 // It's killed successfully, but we'd make sure the cleanup work is done.
-                synchronized (precedence) {
-                    if (app.mPrecedence != null) {
+                synchronized (predecessor) {
+                    if (app.mPredecessor != null) {
                         now = System.currentTimeMillis();
                         if (now < end) {
                             try {
-                                precedence.wait(end - now);
+                                predecessor.wait(end - now);
                             } catch (InterruptedException e) {
                             }
                         }
                     }
-                    if (app.mPrecedence != null) {
+                    if (app.mPredecessor != null) {
                         // The cleanup work hasn't be done yet, let's log it and continue.
-                        Slog.w(TAG, precedence + " has died, but its cleanup isn't done");
+                        Slog.w(TAG, predecessor + " has died, but its cleanup isn't done");
                     }
                 }
             } catch (Exception e) {
                 // It's still alive... maybe blocked at uninterruptible sleep ?
-                Slog.wtf(TAG, precedence.toString() + " refused to die, but we need to launch "
+                Slog.wtf(TAG, predecessor.toString() + " refused to die, but we need to launch "
                         + app, e);
             } finally {
                 StrictMode.setThreadPolicyMask(oldPolicy);
             }
         }
         try {
-            final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
-                    entryPoint, app, app.startUid, gids, runtimeFlags, zygotePolicyFlags,
-                    mountExternal, app.seInfo, requiredAbi, instructionSet, invokeWith,
-                    app.startTime);
+            final Process.ProcessStartResult startResult = startProcess(app.getHostingRecord(),
+                    entryPoint, app, app.getStartUid(), gids, runtimeFlags, zygotePolicyFlags,
+                    mountExternal, app.getSeInfo(), requiredAbi, instructionSet, invokeWith,
+                    app.getStartTime());
 
             synchronized (mService) {
                 handleProcessStartedLocked(app, startResult, startSeq);
@@ -2109,7 +2177,7 @@
                 Slog.e(ActivityManagerService.TAG, "Failure starting process "
                         + app.processName, e);
                 mPendingStarts.remove(startSeq);
-                app.pendingStart = false;
+                app.setPendingStart(false);
                 mService.forceStopPackageLocked(app.info.packageName,
                         UserHandle.getAppId(app.uid),
                         false, false, true, false, false, app.userId, "start failure");
@@ -2135,19 +2203,19 @@
         // Free the isolated uid for this process
         final IsolatedUidRange appUidRange =
                 mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(app.info.processName,
-                        app.hostingRecord.getDefiningUid());
+                        app.getHostingRecord().getDefiningUid());
         if (appUidRange != null) {
             appUidRange.freeIsolatedUidLocked(app.uid);
         }
 
         final AppZygote appZygote = mAppZygotes.get(app.info.processName,
-                app.hostingRecord.getDefiningUid());
+                app.getHostingRecord().getDefiningUid());
         if (appZygote != null) {
             ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
             zygoteProcesses.remove(app);
             if (zygoteProcesses.size() == 0) {
                 mService.mHandler.removeMessages(KILL_APP_ZYGOTE_MSG);
-                if (app.removed) {
+                if (app.isRemoved()) {
                     // If we stopped this process because the package hosting it was removed,
                     // there's no point in delaying the app zygote kill.
                     killAppZygoteIfNeededLocked(appZygote, false /* force */);
@@ -2164,7 +2232,7 @@
         synchronized (mService) {
             // The UID for the app zygote should be the UID of the application hosting
             // the service.
-            final int uid = app.hostingRecord.getDefiningUid();
+            final int uid = app.getHostingRecord().getDefiningUid();
             AppZygote appZygote = mAppZygotes.get(app.info.processName, uid);
             final ArrayList<ProcessRecord> zygoteProcessList;
             if (appZygote == null) {
@@ -2173,7 +2241,7 @@
                 }
                 final IsolatedUidRange uidRange =
                         mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(
-                                app.info.processName, app.hostingRecord.getDefiningUid());
+                                app.info.processName, app.getHostingRecord().getDefiningUid());
                 final int userId = UserHandle.getUserId(uid);
                 // Create the app-zygote and provide it with the UID-range it's allowed
                 // to setresuid/setresgid to.
@@ -2186,7 +2254,7 @@
                 // packageName and uid of the defining application. This is because the
                 // preloading only makes sense in the context of the defining application,
                 // not the calling one.
-                appInfo.packageName = app.hostingRecord.getDefiningPackageName();
+                appInfo.packageName = app.getHostingRecord().getDefiningPackageName();
                 appInfo.uid = uid;
                 appZygote = new AppZygote(appInfo, uid, firstUid, lastUid);
                 mAppZygotes.put(app.info.processName, uid, appZygote);
@@ -2233,14 +2301,15 @@
 
     private boolean needsStorageDataIsolation(StorageManagerInternal storageManagerInternal,
             ProcessRecord app) {
+        final int mountMode = app.getMountMode();
         return mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid)
                 && !storageManagerInternal.isExternalStorageService(app.uid)
                 // Special mounting mode doesn't need to have data isolation as they won't
                 // access /mnt/user anyway.
-                && app.mountMode != Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE
-                && app.mountMode != Zygote.MOUNT_EXTERNAL_PASS_THROUGH
-                && app.mountMode != Zygote.MOUNT_EXTERNAL_INSTALLER
-                && app.mountMode != Zygote.MOUNT_EXTERNAL_NONE;
+                && mountMode != Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE
+                && mountMode != Zygote.MOUNT_EXTERNAL_PASS_THROUGH
+                && mountMode != Zygote.MOUNT_EXTERNAL_INSTALLER
+                && mountMode != Zygote.MOUNT_EXTERNAL_NONE;
     }
 
     private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
@@ -2256,7 +2325,7 @@
                 // Use has-foreground-activities as a temporary hint so the current scheduling
                 // group won't be lost when the process is attaching. The actual state will be
                 // refreshed when computing oom-adj.
-                app.setHasForegroundActivities(true);
+                app.mState.setHasForegroundActivities(true);
             }
 
             Map<String, Pair<String, Long>> pkgDataInfoMap;
@@ -2306,7 +2375,7 @@
                         app.processName)) {
                     // Cannot prepare Android/app and Android/obb directory or inode == 0,
                     // so we won't mount it in zygote, but resume the mount after unlocking device.
-                    app.bindMountPending = true;
+                    app.setBindMountPending(true);
                     bindMountAppStorageDirs = false;
                 }
             }
@@ -2323,8 +2392,9 @@
                 startResult = startWebView(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
-                        app.info.dataDir, null, app.info.packageName, app.mDisabledCompatChanges,
-                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
+                        app.info.dataDir, null, app.info.packageName,
+                        app.getDisabledCompatChanges(),
+                        new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
             } else if (hostingRecord.usesAppZygote()) {
                 final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
 
@@ -2334,17 +2404,17 @@
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, null, app.info.packageName,
                         /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
-                        app.mDisabledCompatChanges, pkgDataInfoMap, allowlistedAppDataInfoMap,
+                        app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap,
                         false, false,
-                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
+                        new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
             } else {
                 startResult = Process.start(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
-                        isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
+                        isTopApp, app.getDisabledCompatChanges(), pkgDataInfoMap,
                         allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
-                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
+                        new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
             }
             checkSlow(startTime, "startProcess: returned from zygote!");
             return startResult;
@@ -2359,14 +2429,14 @@
     }
 
     @GuardedBy("mService")
-    final boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
+    boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
             int zygotePolicyFlags, String abiOverride) {
         return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
                 false /* disableHiddenApiChecks */, abiOverride);
     }
 
     @GuardedBy("mService")
-    final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
+    ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
             boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
             int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
             boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs,
@@ -2399,7 +2469,7 @@
                             info.processName);
                     mService.mAppErrors.clearBadProcess(processName, info.uid);
                     if (app != null) {
-                        app.bad = false;
+                        app.mErrorState.setBad(false);
                     }
                 }
             }
@@ -2416,11 +2486,11 @@
         //     already running.
         if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
                 + " app=" + app + " knownToBeDead=" + knownToBeDead
-                + " thread=" + (app != null ? app.thread : null)
-                + " pid=" + (app != null ? app.pid : -1));
-        ProcessRecord precedence = null;
-        if (app != null && app.pid > 0) {
-            if ((!knownToBeDead && !app.killed) || app.thread == null) {
+                + " thread=" + (app != null ? app.getThread() : null)
+                + " pid=" + (app != null ? app.getPid() : -1));
+        ProcessRecord predecessor = null;
+        if (app != null && app.getPid() > 0) {
+            if ((!knownToBeDead && !app.isKilled()) || app.getThread() == null) {
                 // We already have the app running, or are waiting for it to
                 // come up (we have a pid but not yet its thread), so keep it.
                 if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
@@ -2434,12 +2504,12 @@
             // clean it up now.
             if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app);
             checkSlow(startTime, "startProcess: bad proc running, killing");
-            ProcessList.killProcessGroup(app.uid, app.pid);
+            ProcessList.killProcessGroup(app.uid, app.getPid());
             checkSlow(startTime, "startProcess: done killing old proc");
 
-            if (!app.killed
+            if (!app.isKilled()
                     || mService.mAppProfiler.isLastMemoryLevelNormal()
-                    || app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY
+                    || app.mState.getSetProcState() < ActivityManager.PROCESS_STATE_CACHED_EMPTY
                     || app.mProfile.getLastCachedPss() < getCachedRestoreThresholdKb()) {
                 // Throw a wtf if it's not killed, or killed but not because the system was in
                 // memory pressure + the app was in "cch-empty" and used large amount of memory
@@ -2448,8 +2518,8 @@
                 Slog.w(TAG_PROCESSES, app.toString() + " is attached to a previous process");
             }
             // We are not going to re-use the ProcessRecord, as we haven't dealt with the cleanup
-            // routine of it yet, but we'd set it as the precedence of the new process.
-            precedence = app;
+            // routine of it yet, but we'd set it as the predecessor of the new process.
+            predecessor = app;
             app = null;
         }
 
@@ -2461,12 +2531,12 @@
                         + processName + "/" + info.uid + " isolated=" + isolated);
                 return null;
             }
-            app.crashHandler = crashHandler;
-            app.isolatedEntryPoint = entryPoint;
-            app.isolatedEntryPointArgs = entryPointArgs;
-            if (precedence != null) {
-                app.mPrecedence = precedence;
-                precedence.mSuccessor = app;
+            app.mErrorState.setCrashHandler(crashHandler);
+            app.setIsolatedEntryPoint(entryPoint);
+            app.setIsolatedEntryPointArgs(entryPointArgs);
+            if (predecessor != null) {
+                app.mPredecessor = predecessor;
+                predecessor.mSuccessor = app;
             }
             checkSlow(startTime, "startProcess: done creating new process record");
         } else {
@@ -2499,7 +2569,7 @@
     @GuardedBy("mService")
     private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
         StringBuilder sb = null;
-        if (app.killedByAm) {
+        if (app.isKilledByAm()) {
             if (sb == null) sb = new StringBuilder();
             sb.append("killedByAm=true;");
         }
@@ -2507,13 +2577,13 @@
             if (sb == null) sb = new StringBuilder();
             sb.append("No entry in mProcessNames;");
         }
-        if (!app.pendingStart) {
+        if (!app.isPendingStart()) {
             if (sb == null) sb = new StringBuilder();
             sb.append("pendingStart=false;");
         }
-        if (app.startSeq > expectedStartSeq) {
+        if (app.getStartSeq() > expectedStartSeq) {
             if (sb == null) sb = new StringBuilder();
-            sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
+            sb.append("seq=" + app.getStartSeq() + ",expected=" + expectedStartSeq + ";");
         }
         try {
             AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, app.userId);
@@ -2536,7 +2606,7 @@
             Process.ProcessStartResult startResult, long expectedStartSeq) {
         // Indicates that this process start has been taken care of.
         if (mPendingStarts.get(expectedStartSeq) == null) {
-            if (pending.pid == startResult.pid) {
+            if (pending.getPid() == startResult.pid) {
                 pending.setUsingWrapper(startResult.usingWrapper);
                 // TODO: Update already existing clients of usingWrapper
             }
@@ -2555,31 +2625,31 @@
             Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" +
                     pid
                     + ", " + reason);
-            app.pendingStart = false;
+            app.setPendingStart(false);
             killProcessQuiet(pid);
-            Process.killProcessGroup(app.uid, app.pid);
+            Process.killProcessGroup(app.uid, app.getPid());
             noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
                     ApplicationExitInfo.SUBREASON_INVALID_START, reason);
             return false;
         }
         mService.mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
-        checkSlow(app.startTime, "startProcess: done updating battery stats");
+        checkSlow(app.getStartTime(), "startProcess: done updating battery stats");
 
         EventLog.writeEvent(EventLogTags.AM_PROC_START,
-                UserHandle.getUserId(app.startUid), pid, app.startUid,
-                app.processName, app.hostingRecord.getType(),
-                app.hostingRecord.getName() != null ? app.hostingRecord.getName() : "");
+                UserHandle.getUserId(app.getStartUid()), pid, app.getStartUid(),
+                app.processName, app.getHostingRecord().getType(),
+                app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : "");
 
         try {
             AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.info.packageName,
-                    app.processName, app.uid, app.seInfo, app.info.sourceDir, pid);
+                    app.processName, app.uid, app.getSeInfo(), app.info.sourceDir, pid);
         } catch (RemoteException ex) {
             // Ignore
         }
 
         Watchdog.getInstance().processStarted(app.processName, pid);
 
-        checkSlow(app.startTime, "startProcess: building log message");
+        checkSlow(app.getStartTime(), "startProcess: building log message");
         StringBuilder buf = mStringBuilder;
         buf.setLength(0);
         buf.append("Start proc ");
@@ -2587,23 +2657,25 @@
         buf.append(':');
         buf.append(app.processName);
         buf.append('/');
-        UserHandle.formatUid(buf, app.startUid);
-        if (app.isolatedEntryPoint != null) {
+        UserHandle.formatUid(buf, app.getStartUid());
+        if (app.getIsolatedEntryPoint() != null) {
             buf.append(" [");
-            buf.append(app.isolatedEntryPoint);
+            buf.append(app.getIsolatedEntryPoint());
             buf.append("]");
         }
         buf.append(" for ");
-        buf.append(app.hostingRecord.getType());
-        if (app.hostingRecord.getName() != null) {
+        buf.append(app.getHostingRecord().getType());
+        if (app.getHostingRecord().getName() != null) {
             buf.append(" ");
-            buf.append(app.hostingRecord.getName());
+            buf.append(app.getHostingRecord().getName());
         }
-        mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
-        app.setPid(pid);
-        app.setUsingWrapper(usingWrapper);
-        app.pendingStart = false;
-        checkSlow(app.startTime, "startProcess: starting to update pids map");
+        mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.getStartUid());
+        synchronized (mProcLock) {
+            app.setPid(pid);
+            app.setUsingWrapper(usingWrapper);
+            app.setPendingStart(false);
+        }
+        checkSlow(app.getStartTime(), "startProcess: starting to update pids map");
         ProcessRecord oldApp;
         synchronized (mService.mPidsSelfLocked) {
             oldApp = mService.mPidsSelfLocked.get(pid);
@@ -2612,11 +2684,11 @@
         if (oldApp != null && !app.isolated) {
             // Clean up anything relating to this pid first
             Slog.wtf(TAG, "handleProcessStartedLocked process:" + app.processName
-                    + " startSeq:" + app.startSeq
+                    + " startSeq:" + app.getStartSeq()
                     + " pid:" + pid
                     + " belongs to another existing app:" + oldApp.processName
-                    + " startSeq:" + oldApp.startSeq);
-            mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
+                    + " startSeq:" + oldApp.getStartSeq());
+            mService.cleanUpApplicationRecordLocked(oldApp, pid, false, false, -1,
                     true /*replacingPid*/);
         }
         mService.addPidLocked(app);
@@ -2628,43 +2700,46 @@
                         ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
             }
         }
-        checkSlow(app.startTime, "startProcess: done updating pids map");
+        checkSlow(app.getStartTime(), "startProcess: done updating pids map");
         return true;
     }
 
-    final void removeLruProcessLocked(ProcessRecord app) {
+    @GuardedBy("mService")
+    void removeLruProcessLocked(ProcessRecord app) {
         int lrui = mLruProcesses.lastIndexOf(app);
         if (lrui >= 0) {
-            if (!app.killed) {
-                if (app.isPersistent()) {
-                    Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
-                } else {
-                    Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
-                    if (app.pid > 0) {
-                        killProcessQuiet(app.pid);
-                        ProcessList.killProcessGroup(app.uid, app.pid);
-                        noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
-                                ApplicationExitInfo.SUBREASON_REMOVE_LRU, "hasn't been killed");
+            synchronized (mProcLock) {
+                if (!app.isKilled()) {
+                    if (app.isPersistent()) {
+                        Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
                     } else {
-                        app.pendingStart = false;
+                        Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
+                        if (app.getPid() > 0) {
+                            killProcessQuiet(app.getPid());
+                            ProcessList.killProcessGroup(app.uid, app.getPid());
+                            noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
+                                    ApplicationExitInfo.SUBREASON_REMOVE_LRU, "hasn't been killed");
+                        } else {
+                            app.setPendingStart(false);
+                        }
                     }
                 }
+                if (lrui < mLruProcessActivityStart) {
+                    mLruProcessActivityStart--;
+                }
+                if (lrui < mLruProcessServiceStart) {
+                    mLruProcessServiceStart--;
+                }
+                mLruProcesses.remove(lrui);
             }
-            if (lrui < mLruProcessActivityStart) {
-                mLruProcessActivityStart--;
-            }
-            if (lrui < mLruProcessServiceStart) {
-                mLruProcessServiceStart--;
-            }
-            mLruProcesses.remove(lrui);
-            mService.removeOomAdjTargetLocked(app, true);
         }
+        mService.removeOomAdjTargetLocked(app, true);
     }
 
-    @GuardedBy("mService")
-    boolean killPackageProcessesLocked(String packageName, int appId, int userId, int minOomAdj,
+    @GuardedBy({"mService", "mProcLock"})
+    boolean killPackageProcessesLSP(String packageName, int appId, int userId, int minOomAdj,
             int reasonCode, int subReason, String reason) {
-        return killPackageProcessesLocked(packageName, appId, userId, minOomAdj,
+        return killPackageProcessesLSP(packageName, appId, userId, minOomAdj,
                 false /* callerWillRestart */, true /* allowRestart */, true /* doit */,
                 false /* evenPersistent */, false /* setRemoved */, reasonCode,
                 subReason, reason);
@@ -2697,8 +2772,8 @@
         }
     }
 
-    @GuardedBy("mService")
-    final boolean killPackageProcessesLocked(String packageName, int appId,
+    @GuardedBy({"mService", "mProcLock"})
+    boolean killPackageProcessesLSP(String packageName, int appId,
             int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
             boolean doit, boolean evenPersistent, boolean setRemoved, int reasonCode,
             int subReason, String reason) {
@@ -2717,7 +2792,7 @@
                     // we don't kill persistent processes
                     continue;
                 }
-                if (app.removed) {
+                if (app.isRemoved()) {
                     if (doit) {
                         procs.add(app);
                     }
@@ -2725,7 +2800,7 @@
                 }
 
                 // Skip process if it doesn't meet our oom adj requirement.
-                if (app.setAdj < minOomAdj) {
+                if (app.mState.getSetAdj() < minOomAdj) {
                     // Note it is still possible to have a process with oom adj 0 in the killed
                     // processes, but it does not mean misjudgment. E.g. a bound service process
                     // and its client activity process are both in the background, so they are
@@ -2747,8 +2822,8 @@
                     // that match it.  We need to qualify this by the processes
                     // that are running under the specified app and user ID.
                 } else {
-                    final boolean isDep = app.pkgDeps != null
-                            && app.pkgDeps.contains(packageName);
+                    final boolean isDep = app.getPkgDeps() != null
+                            && app.getPkgDeps().contains(packageName);
                     if (!isDep && UserHandle.getAppId(app.uid) != appId) {
                         continue;
                     }
@@ -2765,7 +2840,7 @@
                     return true;
                 }
                 if (setRemoved) {
-                    app.removed = true;
+                    app.setRemoved(true);
                 }
                 procs.add(app);
             }
@@ -2806,12 +2881,12 @@
         mService.mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
 
         boolean needRestart = false;
-        if ((app.pid > 0 && app.pid != ActivityManagerService.MY_PID) || (app.pid == 0 && app
-                .pendingStart)) {
-            int pid = app.pid;
+        final int pid = app.getPid();
+        if ((pid > 0 && pid != ActivityManagerService.MY_PID)
+                || (pid == 0 && app.isPendingStart())) {
             if (pid > 0) {
-                mService.removePidLocked(app);
-                app.bindMountPending = false;
+                mService.removePidLocked(pid, app);
+                app.setBindMountPending(false);
                 mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
                 mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
                 if (app.isolated) {
@@ -2827,8 +2902,8 @@
                     needRestart = true;
                 }
             }
-            app.kill(reason, reasonCode, subReason, true);
-            mService.handleAppDiedLocked(app, willRestart, allowRestart);
+            app.killLocked(reason, reasonCode, subReason, true);
+            mService.handleAppDiedLocked(app, pid, willRestart, allowRestart);
             if (willRestart) {
                 removeLruProcessLocked(app);
                 mService.addAppLocked(app.info, null, false, null /* ABI override */,
@@ -2842,48 +2917,51 @@
     }
 
     @GuardedBy("mService")
-    final void addProcessNameLocked(ProcessRecord proc) {
+    void addProcessNameLocked(ProcessRecord proc) {
         // We shouldn't already have a process under this name, but just in case we
         // need to clean up whatever may be there now.
-        ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
-        if (old == proc && proc.isPersistent()) {
-            // We are re-adding a persistent process.  Whatevs!  Just leave it there.
-            Slog.w(TAG, "Re-adding persistent process " + proc);
-        } else if (old != null) {
-            if (old.killed) {
-                // The old process has been killed, we probably haven't had
-                // a chance to clean up the old record, just log a warning
-                Slog.w(TAG, "Existing proc " + old + " was killed "
-                        + (SystemClock.uptimeMillis() - old.mKillTime)
-                        + "ms ago when adding " + proc);
-            } else {
-                Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+        synchronized (mProcLock) {
+            ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
+            if (old == proc && proc.isPersistent()) {
+                // We are re-adding a persistent process.  Whatevs!  Just leave it there.
+                Slog.w(TAG, "Re-adding persistent process " + proc);
+            } else if (old != null) {
+                if (old.isKilled()) {
+                    // The old process has been killed, we probably haven't had
+                    // a chance to clean up the old record, just log a warning
+                    Slog.w(TAG, "Existing proc " + old + " was killed "
+                            + (SystemClock.uptimeMillis() - old.getKillTime())
+                            + "ms ago when adding " + proc);
+                } else {
+                    Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+                }
             }
-        }
-        UidRecord uidRec = mActiveUids.get(proc.uid);
-        if (uidRec == null) {
-            uidRec = new UidRecord(proc.uid);
-            // This is the first appearance of the uid, report it now!
-            if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
-                    "Creating new process uid: " + uidRec);
-            if (Arrays.binarySearch(mService.mDeviceIdleTempAllowlist,
-                    UserHandle.getAppId(proc.uid)) >= 0
-                    || mService.mPendingTempAllowlist.indexOfKey(proc.uid) >= 0) {
-                uidRec.mSetAllowlist = uidRec.mCurAllowlist = true;
+            UidRecord uidRec = mActiveUids.get(proc.uid);
+            if (uidRec == null) {
+                uidRec = new UidRecord(proc.uid, mService);
+                // This is the first appearance of the uid, report it now!
+                if (DEBUG_UID_OBSERVERS) {
+                    Slog.i(TAG_UID_OBSERVERS, "Creating new process uid: " + uidRec);
+                }
+                if (Arrays.binarySearch(mService.mDeviceIdleTempAllowlist,
+                            UserHandle.getAppId(proc.uid)) >= 0
+                        || mService.mPendingTempAllowlist.indexOfKey(proc.uid) >= 0) {
+                    uidRec.setCurAllowListed(true);
+                    uidRec.setSetAllowListed(true);
+                }
+                uidRec.updateHasInternetPermission();
+                mActiveUids.put(proc.uid, uidRec);
+                EventLogTags.writeAmUidRunning(uidRec.getUid());
+                mService.noteUidProcessState(uidRec.getUid(), uidRec.getCurProcState(),
+                        uidRec.getCurCapability());
             }
-            uidRec.updateHasInternetPermission();
-            mActiveUids.put(proc.uid, uidRec);
-            EventLogTags.writeAmUidRunning(uidRec.uid);
-            mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState(),
-                    uidRec.curCapability);
-        }
-        proc.uidRecord = uidRec;
-        uidRec.procRecords.add(proc);
+            proc.setUidRecord(uidRec);
+            uidRec.addProcess(proc);
 
-        // Reset render thread tid if it was already set, so new process can set it again.
-        proc.renderThreadTid = 0;
-        uidRec.numProcs++;
-        mProcessNames.put(proc.processName, proc.uid, proc);
+            // Reset render thread tid if it was already set, so new process can set it again.
+            proc.setRenderThreadTid(0);
+            mProcessNames.put(proc.processName, proc.uid, proc);
+        }
         if (proc.isolated) {
             mIsolatedProcesses.put(proc.uid, proc);
         }
@@ -2902,7 +2980,7 @@
     }
 
     @GuardedBy("mService")
-    final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
+    ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
             boolean isolated, int isolatedUid, HostingRecord hostingRecord) {
         String proc = customProcess != null ? customProcess : info.processName;
         final int userId = UserHandle.getUserId(info.uid);
@@ -2936,58 +3014,63 @@
                     FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
         }
         final ProcessRecord r = new ProcessRecord(mService, info, proc, uid);
+        final ProcessStateRecord state = r.mState;
 
         if (!mService.mBooted && !mService.mBooting
                 && userId == UserHandle.USER_SYSTEM
                 && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
             // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
-            r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
-            r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+            state.setSetSchedGroup(ProcessList.SCHED_GROUP_DEFAULT);
             r.setPersistent(true);
-            r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
+            state.setMaxAdj(ProcessList.PERSISTENT_PROC_ADJ);
         }
         if (isolated && isolatedUid != 0) {
             // Special case for startIsolatedProcess (internal only) - assume the process
             // is required by the system server to prevent it being killed.
-            r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
+            state.setMaxAdj(ProcessList.PERSISTENT_SERVICE_ADJ);
         }
         addProcessNameLocked(r);
         return r;
     }
 
     @GuardedBy("mService")
-    final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
+    ProcessRecord removeProcessNameLocked(final String name, final int uid) {
         return removeProcessNameLocked(name, uid, null);
     }
 
     @GuardedBy("mService")
-    final ProcessRecord removeProcessNameLocked(final String name, final int uid,
+    ProcessRecord removeProcessNameLocked(final String name, final int uid,
             final ProcessRecord expecting) {
         ProcessRecord old = mProcessNames.get(name, uid);
-        // Only actually remove when the currently recorded value matches the
-        // record that we expected; if it doesn't match then we raced with a
-        // newly created process and we don't want to destroy the new one.
-        if ((expecting == null) || (old == expecting)) {
-            mProcessNames.remove(name, uid);
-        }
         final ProcessRecord record = expecting != null ? expecting : old;
-        if (record != null && record.uidRecord != null) {
-            final UidRecord uidRecord = record.uidRecord;
-            uidRecord.numProcs--;
-            uidRecord.procRecords.remove(record);
-            if (uidRecord.numProcs == 0) {
-                // No more processes using this uid, tell clients it is gone.
-                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
-                        "No more processes in " + uidRecord);
-                mService.enqueueUidChangeLocked(uidRecord, -1,
-                        UidRecord.CHANGE_GONE);
-                EventLogTags.writeAmUidStopped(uid);
-                mActiveUids.remove(uid);
-                mService.mFgsStartTempAllowList.remove(record.info.uid);
-                mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT,
-                        ActivityManager.PROCESS_CAPABILITY_NONE);
+        synchronized (mProcLock) {
+            // Only actually remove when the currently recorded value matches the
+            // record that we expected; if it doesn't match then we raced with a
+            // newly created process and we don't want to destroy the new one.
+            if ((expecting == null) || (old == expecting)) {
+                mProcessNames.remove(name, uid);
             }
-            record.uidRecord = null;
+            if (record != null) {
+                final UidRecord uidRecord = record.getUidRecord();
+                if (uidRecord != null) {
+                    uidRecord.removeProcess(record);
+                    if (uidRecord.getNumOfProcs() == 0) {
+                        // No more processes using this uid, tell clients it is gone.
+                        if (DEBUG_UID_OBSERVERS) {
+                            Slog.i(TAG_UID_OBSERVERS, "No more processes in " + uidRecord);
+                        }
+                        mService.enqueueUidChangeLocked(uidRecord, -1,
+                                UidRecord.CHANGE_GONE);
+                        EventLogTags.writeAmUidStopped(uid);
+                        mActiveUids.remove(uid);
+                        mService.mFgsStartTempAllowList.remove(record.info.uid);
+                        mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT,
+                                ActivityManager.PROCESS_CAPABILITY_NONE);
+                    }
+                    record.setUidRecord(null);
+                }
+            }
         }
         mIsolatedProcesses.remove(uid);
         mGlobalIsolatedUids.freeIsolatedUidLocked(uid);
@@ -3000,13 +3083,14 @@
     }
 
     /** Call setCoreSettings on all LRU processes, with the new settings. */
-    @GuardedBy("mService")
-    void updateCoreSettingsLocked(Bundle settings) {
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    void updateCoreSettingsLOSP(Bundle settings) {
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord processRecord = mLruProcesses.get(i);
+            final IApplicationThread thread = processRecord.getThread();
             try {
-                if (processRecord.thread != null) {
-                    processRecord.thread.setCoreSettings(settings);
+                if (thread != null) {
+                    thread.setCoreSettings(settings);
                 }
             } catch (RemoteException re) {
                 /* ignore */
@@ -3020,8 +3104,8 @@
      * @param minTargetSdk
      * @param maxProcState
      */
-    @GuardedBy("mService")
-    void killAllBackgroundProcessesExceptLocked(int minTargetSdk, int maxProcState) {
+    @GuardedBy({"mService", "mProcLock"})
+    void killAllBackgroundProcessesExceptLSP(int minTargetSdk, int maxProcState) {
         final ArrayList<ProcessRecord> procs = new ArrayList<>();
         final int NP = mProcessNames.getMap().size();
         for (int ip = 0; ip < NP; ip++) {
@@ -3029,8 +3113,9 @@
             final int NA = apps.size();
             for (int ia = 0; ia < NA; ia++) {
                 final ProcessRecord app = apps.valueAt(ia);
-                if (app.removed || ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
-                        && (maxProcState < 0 || app.setProcState > maxProcState))) {
+                if (app.isRemoved()
+                        || ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
+                        && (maxProcState < 0 || app.mState.getSetProcState() > maxProcState))) {
                     procs.add(app);
                 }
             }
@@ -3047,13 +3132,14 @@
      * Call updateTimePrefs on all LRU processes
      * @param timePref The time pref to pass to each process
      */
-    @GuardedBy("mService")
-    void updateAllTimePrefsLocked(int timePref) {
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    void updateAllTimePrefsLOSP(int timePref) {
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord r = mLruProcesses.get(i);
-            if (r.thread != null) {
+            final IApplicationThread thread = r.getThread();
+            if (thread != null) {
                 try {
-                    r.thread.updateTimePrefs(timePref);
+                    thread.updateTimePrefs(timePref);
                 } catch (RemoteException ex) {
                     Slog.w(TAG, "Failed to update preferences for: "
                             + r.info.processName);
@@ -3064,15 +3150,16 @@
 
     void setAllHttpProxy() {
         // Update the HTTP proxy for each application thread.
-        synchronized (mService) {
+        synchronized (mProcLock) {
             for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
                 ProcessRecord r = mLruProcesses.get(i);
+                IApplicationThread thread = r.getThread();
                 // Don't dispatch to isolated processes as they can't access ConnectivityManager and
                 // don't have network privileges anyway. Exclude system server and update it
                 // separately outside the AMS lock, to avoid deadlock with Connectivity Service.
-                if (r.pid != ActivityManagerService.MY_PID && r.thread != null && !r.isolated) {
+                if (r.getPid() != ActivityManagerService.MY_PID && thread != null && !r.isolated) {
                     try {
-                        r.thread.updateHttpProxy();
+                        thread.updateHttpProxy();
                     } catch (RemoteException ex) {
                         Slog.w(TAG, "Failed to update http proxy for: "
                                 + r.info.processName);
@@ -3083,13 +3170,14 @@
         ActivityThread.updateHttpProxy(mService.mContext);
     }
 
-    @GuardedBy("mService")
-    void clearAllDnsCacheLocked() {
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    void clearAllDnsCacheLOSP() {
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord r = mLruProcesses.get(i);
-            if (r.thread != null) {
+            final IApplicationThread thread = r.getThread();
+            if (thread != null) {
                 try {
-                    r.thread.clearDnsCache();
+                    thread.clearDnsCache();
                 } catch (RemoteException ex) {
                     Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
                 }
@@ -3097,13 +3185,14 @@
         }
     }
 
-    @GuardedBy("mService")
-    void handleAllTrustStorageUpdateLocked() {
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    void handleAllTrustStorageUpdateLOSP() {
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord r = mLruProcesses.get(i);
-            if (r.thread != null) {
+            final IApplicationThread thread = r.getThread();
+            if (thread != null) {
                 try {
-                    r.thread.handleTrustStorageUpdate();
+                    thread.handleTrustStorageUpdate();
                 } catch (RemoteException ex) {
                     Slog.w(TAG, "Failed to handle trust storage update for: " +
                             r.info.processName);
@@ -3112,10 +3201,10 @@
         }
     }
 
-    @GuardedBy("mService")
-    int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
+    @GuardedBy({"mService", "mProcLock"})
+    private int updateLruProcessInternalLSP(ProcessRecord app, long now, int index,
             int lruSeq, String what, Object obj, ProcessRecord srcApp) {
-        app.lastActivityTime = now;
+        app.setLastActivityTime(now);
 
         if (app.hasActivitiesOrRecentTasks()) {
             // Don't want to touch dependent processes that are hosting activities.
@@ -3147,7 +3236,7 @@
         if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
                 + " in LRU list: " + app);
         mLruProcesses.add(index, app);
-        app.lruSeq = lruSeq;
+        app.setLruSeq(lruSeq);
         return index;
     }
 
@@ -3167,45 +3256,51 @@
      * @param endIndex The current end of the top being processed.  Typically topI - 1.  That is,
      *                 where we are going to start potentially adjusting other entries in the list.
      */
-    private void updateClientActivitiesOrdering(final ProcessRecord topApp, final int topI,
+    @GuardedBy({"mService", "mProcLock"})
+    private void updateClientActivitiesOrderingLSP(final ProcessRecord topApp, final int topI,
             final int bottomI, int endIndex) {
-        if (topApp.hasActivitiesOrRecentTasks() || topApp.treatLikeActivity
-                || !topApp.hasClientActivities()) {
+        final ProcessServiceRecord topPsr = topApp.mServices;
+        if (topApp.hasActivitiesOrRecentTasks() || topPsr.isTreatedLikeActivity()
+                || !topPsr.hasClientActivities()) {
             // If this is not a special process that has client activities, then there is
             // nothing to do.
             return;
         }
 
         final int uid = topApp.info.uid;
-        if (topApp.connectionGroup > 0) {
-            int endImportance = topApp.connectionImportance;
+        final int topConnectionGroup = topPsr.getConnectionGroup();
+        if (topConnectionGroup > 0) {
+            int endImportance = topPsr.getConnectionImportance();
             for (int i = endIndex; i >= bottomI; i--) {
                 final ProcessRecord subProc = mLruProcesses.get(i);
+                final ProcessServiceRecord subPsr = subProc.mServices;
+                final int subConnectionGroup = subPsr.getConnectionGroup();
+                final int subConnectionImportance = subPsr.getConnectionImportance();
                 if (subProc.info.uid == uid
-                        && subProc.connectionGroup == topApp.connectionGroup) {
-                    if (i == endIndex && subProc.connectionImportance >= endImportance) {
+                        && subConnectionGroup == topConnectionGroup) {
+                    if (i == endIndex && subConnectionImportance >= endImportance) {
                         // This process is already in the group, and its importance
                         // is not as strong as the process before it, so keep it
                         // correctly positioned in the group.
                         if (DEBUG_LRU) Slog.d(TAG_LRU,
                                 "Keeping in-place above " + subProc
                                         + " endImportance=" + endImportance
-                                        + " group=" + subProc.connectionGroup
-                                        + " importance=" + subProc.connectionImportance);
+                                        + " group=" + subConnectionGroup
+                                        + " importance=" + subConnectionImportance);
                         endIndex--;
-                        endImportance = subProc.connectionImportance;
+                        endImportance = subConnectionImportance;
                     } else {
                         // We want to pull this up to be with the rest of the group,
                         // and order within the group by importance.
                         if (DEBUG_LRU) Slog.d(TAG_LRU,
                                 "Pulling up " + subProc
                                         + " to position in group with importance="
-                                        + subProc.connectionImportance);
+                                        + subConnectionImportance);
                         boolean moved = false;
                         for (int pos = topI; pos > endIndex; pos--) {
                             final ProcessRecord posProc = mLruProcesses.get(pos);
-                            if (subProc.connectionImportance
-                                    <= posProc.connectionImportance) {
+                            if (subConnectionImportance
+                                    <= posProc.mServices.getConnectionImportance()) {
                                 mLruProcesses.remove(i);
                                 mLruProcesses.add(pos, subProc);
                                 if (DEBUG_LRU) Slog.d(TAG_LRU,
@@ -3226,7 +3321,7 @@
                                             + " from position " + i + " to end of group @ "
                                             + endIndex);
                             endIndex--;
-                            endImportance = subProc.connectionImportance;
+                            endImportance = subConnectionImportance;
                         }
                     }
                 }
@@ -3238,6 +3333,8 @@
         int i = endIndex;
         while (i >= bottomI) {
             ProcessRecord subProc = mLruProcesses.get(i);
+            final ProcessServiceRecord subPsr = subProc.mServices;
+            final int subConnectionGroup = subPsr.getConnectionGroup();
             if (DEBUG_LRU) Slog.d(TAG_LRU,
                     "Looking to spread old procs, at " + subProc + " @ " + i);
             if (subProc.info.uid != uid) {
@@ -3260,7 +3357,8 @@
                         subProc = mLruProcesses.get(i);
                         if (DEBUG_LRU) Slog.d(TAG_LRU,
                                 "Looking at next app at " + i + ": " + subProc);
-                        if (subProc.hasActivitiesOrRecentTasks() || subProc.treatLikeActivity) {
+                        if (subProc.hasActivitiesOrRecentTasks()
+                                || subPsr.isTreatedLikeActivity()) {
                             if (DEBUG_LRU) Slog.d(TAG_LRU,
                                     "This is hosting an activity!");
                             if (hasActivity) {
@@ -3270,7 +3368,7 @@
                                 break;
                             }
                             hasActivity = true;
-                        } else if (subProc.hasClientActivities()) {
+                        } else if (subPsr.hasClientActivities()) {
                             if (DEBUG_LRU) Slog.d(TAG_LRU,
                                     "This is a client of an activity");
                             if (hasActivity) {
@@ -3281,21 +3379,21 @@
                                             "Already found a different activity: connUid="
                                             + connUid + " uid=" + subProc.info.uid);
                                     break;
-                                } else if (connGroup == 0 || connGroup != subProc.connectionGroup) {
+                                } else if (connGroup == 0 || connGroup != subConnectionGroup) {
                                     // Previously saw a different group or not from a group,
                                     // want to treat these as different things.
                                     if (DEBUG_LRU) Slog.d(TAG_LRU,
                                             "Already found a different group: connGroup="
-                                            + connGroup + " group=" + subProc.connectionGroup);
+                                            + connGroup + " group=" + subConnectionGroup);
                                     break;
                                 }
                             } else {
                                 if (DEBUG_LRU) Slog.d(TAG_LRU,
                                         "This is an activity client!  uid="
-                                        + subProc.info.uid + " group=" + subProc.connectionGroup);
+                                        + subProc.info.uid + " group=" + subConnectionGroup);
                                 hasActivity = true;
                                 connUid = subProc.info.uid;
-                                connGroup = subProc.connectionGroup;
+                                connGroup = subConnectionGroup;
                             }
                         }
                         endIndex--;
@@ -3316,13 +3414,16 @@
                 }
                 if (endIndex >= bottomI) {
                     final ProcessRecord endProc = mLruProcesses.get(endIndex);
+                    final ProcessServiceRecord endPsr = endProc.mServices;
+                    final int endConnectionGroup = endPsr.getConnectionGroup();
                     for (endIndex--; endIndex >= bottomI; endIndex--) {
                         final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
+                        final int nextConnectionGroup = nextEndProc.mServices.getConnectionGroup();
                         if (nextEndProc.info.uid != uid
-                                || nextEndProc.connectionGroup != endProc.connectionGroup) {
+                                || nextConnectionGroup != endConnectionGroup) {
                             if (DEBUG_LRU) Slog.d(TAG_LRU,
                                     "Found next group or app: " + nextEndProc + " @ "
-                                            + endIndex + " group=" + nextEndProc.connectionGroup);
+                                            + endIndex + " group=" + nextConnectionGroup);
                             break;
                         }
                     }
@@ -3336,10 +3437,11 @@
         }
     }
 
-    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
-            ProcessRecord client) {
-        final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities()
-                || app.treatLikeActivity;
+    @GuardedBy("mService")
+    void updateLruProcessLocked(ProcessRecord app, boolean activityChange, ProcessRecord client) {
+        final ProcessServiceRecord psr = app.mServices;
+        final boolean hasActivity = app.hasActivitiesOrRecentTasks() || psr.hasClientActivities()
+                || psr.isTreatedLikeActivity();
         final boolean hasService = false; // not impl yet. app.services.size() > 0;
         if (!activityChange && hasActivity) {
             // The process has activities, so we are only allowing activity-based adjustments
@@ -3349,9 +3451,18 @@
             return;
         }
 
+        synchronized (mProcLock) {
+            updateLruProcessLSP(app, client, hasActivity, hasService);
+        }
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    private void updateLruProcessLSP(ProcessRecord app, ProcessRecord client,
+            boolean hasActivity, boolean hasService) {
         mLruSeq++;
         final long now = SystemClock.uptimeMillis();
-        app.lastActivityTime = now;
+        final ProcessServiceRecord psr = app.mServices;
+        app.setLastActivityTime(now);
 
         // First a quick reject: if the app is already at the position we will
         // put it, then there is nothing to do.
@@ -3445,14 +3556,14 @@
         if (hasActivity) {
             final int N = mLruProcesses.size();
             nextIndex = mLruProcessServiceStart;
-            if (!app.hasActivitiesOrRecentTasks() && !app.treatLikeActivity
+            if (!app.hasActivitiesOrRecentTasks() && !psr.isTreatedLikeActivity()
                     && mLruProcessActivityStart < (N - 1)) {
                 // Process doesn't have activities, but has clients with
                 // activities...  move it up, but below the app that is binding to it.
                 if (DEBUG_LRU) Slog.d(TAG_LRU,
                         "Adding to second-top of LRU activity list: " + app
-                        + " group=" + app.connectionGroup
-                        + " importance=" + app.connectionImportance);
+                        + " group=" + psr.getConnectionGroup()
+                        + " importance=" + psr.getConnectionImportance());
                 int pos = N - 1;
                 while (pos > mLruProcessActivityStart) {
                     final ProcessRecord posproc = mLruProcesses.get(pos);
@@ -3472,7 +3583,7 @@
                     endIndex = mLruProcessActivityStart;
                 }
                 nextActivityIndex = endIndex;
-                updateClientActivitiesOrdering(app, pos, mLruProcessActivityStart, endIndex);
+                updateClientActivitiesOrderingLSP(app, pos, mLruProcessActivityStart, endIndex);
             } else {
                 // Process has activities, put it at the very tipsy-top.
                 if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
@@ -3509,46 +3620,48 @@
             mLruProcessActivityStart++;
             mLruProcessServiceStart++;
             if (index > 1) {
-                updateClientActivitiesOrdering(app, mLruProcessServiceStart - 1, 0, index - 1);
+                updateClientActivitiesOrderingLSP(app, mLruProcessServiceStart - 1, 0, index - 1);
             }
         }
 
-        app.lruSeq = mLruSeq;
+        app.setLruSeq(mLruSeq);
 
         // If the app is currently using a content provider or service,
         // bump those processes as well.
-        for (int j = app.connections.size() - 1; j >= 0; j--) {
-            ConnectionRecord cr = app.connections.valueAt(j);
+        for (int j = psr.numberOfConnections() - 1; j >= 0; j--) {
+            ConnectionRecord cr = psr.getConnectionAt(j);
             if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
                     && cr.binding.service.app != null
-                    && cr.binding.service.app.lruSeq != mLruSeq
+                    && cr.binding.service.app.getLruSeq() != mLruSeq
                     && (cr.flags & Context.BIND_REDUCTION_FLAGS) == 0
                     && !cr.binding.service.app.isPersistent()) {
-                if (cr.binding.service.app.hasClientActivities()) {
+                if (cr.binding.service.app.mServices.hasClientActivities()) {
                     if (nextActivityIndex >= 0) {
-                        nextActivityIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+                        nextActivityIndex = updateLruProcessInternalLSP(cr.binding.service.app,
                                 now,
                                 nextActivityIndex, mLruSeq,
                                 "service connection", cr, app);
                     }
                 } else {
-                    nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+                    nextIndex = updateLruProcessInternalLSP(cr.binding.service.app,
                             now,
                             nextIndex, mLruSeq,
                             "service connection", cr, app);
                 }
             }
         }
-        for (int j = app.conProviders.size() - 1; j >= 0; j--) {
-            ContentProviderRecord cpr = app.conProviders.get(j).provider;
-            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
-                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex, mLruSeq,
+        final ProcessProviderRecord ppr = app.mProviders;
+        for (int j = ppr.numberOfProviderConnections() - 1; j >= 0; j--) {
+            ContentProviderRecord cpr = ppr.getProviderConnectionAt(j).provider;
+            if (cpr.proc != null && cpr.proc.getLruSeq() != mLruSeq && !cpr.proc.isPersistent()) {
+                nextIndex = updateLruProcessInternalLSP(cpr.proc, now, nextIndex, mLruSeq,
                         "provider reference", cpr, app);
             }
         }
     }
 
-    final ProcessRecord getLRURecordForAppLocked(IApplicationThread thread) {
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    ProcessRecord getLRURecordForAppLOSP(IApplicationThread thread) {
         if (thread == null) {
             return null;
         }
@@ -3556,18 +3669,20 @@
         // Find the application record.
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             final ProcessRecord rec = mLruProcesses.get(i);
-            if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
+            final IApplicationThread t = rec.getThread();
+            if (t != null && t.asBinder() == threadBinder) {
                 return rec;
             }
         }
         return null;
     }
 
-    boolean haveBackgroundProcessLocked() {
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean haveBackgroundProcessLOSP() {
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             final ProcessRecord rec = mLruProcesses.get(i);
-            if (rec.thread != null
-                    && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
+            if (rec.getThread() != null
+                    && rec.mState.getSetProcState() >= PROCESS_STATE_CACHED_ACTIVITY) {
                 return true;
             }
         }
@@ -3587,11 +3702,11 @@
         return imp;
     }
 
-    @GuardedBy("mService")
-    void fillInProcMemInfoLocked(ProcessRecord app,
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    void fillInProcMemInfoLOSP(ProcessRecord app,
             ActivityManager.RunningAppProcessInfo outInfo,
             int clientTargetSdk) {
-        outInfo.pid = app.pid;
+        outInfo.pid = app.getPid();
         outInfo.uid = app.info.uid;
         if (app.getWindowProcessController().isHeavyWeightProcess()) {
             outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
@@ -3603,49 +3718,53 @@
             outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
         }
         outInfo.lastTrimLevel = app.mProfile.getTrimMemoryLevel();
-        int adj = app.curAdj;
-        int procState = app.getCurProcState();
+        final ProcessStateRecord state = app.mState;
+        int adj = state.getCurAdj();
+        int procState = state.getCurProcState();
         outInfo.importance = procStateToImportance(procState, adj, outInfo,
                 clientTargetSdk);
-        outInfo.importanceReasonCode = app.adjTypeCode;
-        outInfo.processState = app.getCurProcState();
+        outInfo.importanceReasonCode = state.getAdjTypeCode();
+        outInfo.processState = procState;
         outInfo.isFocused = (app == mService.getTopApp());
-        outInfo.lastActivityTime = app.lastActivityTime;
+        outInfo.lastActivityTime = app.getLastActivityTime();
     }
 
-    @GuardedBy("mService")
-    List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesLocked(boolean allUsers,
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesLOSP(boolean allUsers,
             int userId, boolean allUids, int callingUid, int clientTargetSdk) {
         // Lazy instantiation of list
         List<ActivityManager.RunningAppProcessInfo> runList = null;
 
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord app = mLruProcesses.get(i);
+            final ProcessStateRecord state = app.mState;
+            final ProcessErrorStateRecord errState = app.mErrorState;
             if ((!allUsers && app.userId != userId)
                     || (!allUids && app.uid != callingUid)) {
                 continue;
             }
-            if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
+            if ((app.getThread() != null)
+                    && (!errState.isCrashing() && !errState.isNotResponding())) {
                 // Generate process state info for running application
                 ActivityManager.RunningAppProcessInfo currApp =
                         new ActivityManager.RunningAppProcessInfo(app.processName,
-                                app.pid, app.getPackageList());
-                fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
-                if (app.adjSource instanceof ProcessRecord) {
-                    currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
+                                app.getPid(), app.getPackageList());
+                fillInProcMemInfoLOSP(app, currApp, clientTargetSdk);
+                if (state.getAdjSource() instanceof ProcessRecord) {
+                    currApp.importanceReasonPid = ((ProcessRecord) state.getAdjSource()).getPid();
                     currApp.importanceReasonImportance =
                             ActivityManager.RunningAppProcessInfo.procStateToImportance(
-                                    app.adjSourceProcState);
-                } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) {
+                                    state.getAdjSourceProcState());
+                } else if (state.getAdjSource() instanceof ActivityServiceConnectionsHolder) {
                     final ActivityServiceConnectionsHolder r =
-                            (ActivityServiceConnectionsHolder) app.adjSource;
+                            (ActivityServiceConnectionsHolder) state.getAdjSource();
                     final int pid = r.getActivityPid();
                     if (pid != -1) {
                         currApp.importanceReasonPid = pid;
                     }
                 }
-                if (app.adjTarget instanceof ComponentName) {
-                    currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
+                if (state.getAdjTarget() instanceof ComponentName) {
+                    currApp.importanceReasonComponent = (ComponentName) state.getAdjTarget();
                 }
                 //Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
                 //        + " lru=" + currApp.lru);
@@ -3658,11 +3777,110 @@
         return runList;
     }
 
-    @GuardedBy("mService")
-    int getLruSizeLocked() {
+    @GuardedBy(anyOf = {"mService", "mProfileLock"})
+    int getLruSizeLOSP() {
         return mLruProcesses.size();
     }
 
+    /**
+     * Return the reference to the LRU list, call this function for read-only access
+     */
+    @GuardedBy(anyOf = {"mService", "mProfileLock"})
+    ArrayList<ProcessRecord> getLruProcessesLOSP() {
+        return mLruProcesses;
+    }
+
+    /**
+     * Return the reference to the LRU list, call this function for read/write access
+     */
+    @GuardedBy({"mService", "mProfileLock"})
+    ArrayList<ProcessRecord> getLruProcessesLSP() {
+        return mLruProcesses;
+    }
+
+    /**
+     * For test only
+     */
+    @VisibleForTesting
+    @GuardedBy({"mService", "mProfileLock"})
+    void setLruProcessServiceStartLSP(int pos) {
+        mLruProcessServiceStart = pos;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProfileLock"})
+    int getLruProcessServiceStartLOSP() {
+        return mLruProcessServiceStart;
+    }
+
+    /**
+     * Iterate the whole LRU list, invoke the given {@code callback} with each of the ProcessRecord
+     * in that list.
+     *
+     * @param iterateForward If {@code true}, iterate the LRU list from the least recent used
+     *                       to most recent used ProcessRecord.
+     * @param callback The callback interface to accept the current ProcessRecord.
+     */
+    @GuardedBy(anyOf = {"mService", "mProfileLock"})
+    void forEachLruProcessesLOSP(boolean iterateForward,
+            @NonNull Consumer<ProcessRecord> callback) {
+        if (iterateForward) {
+            for (int i = 0, size = mLruProcesses.size(); i < size; i++) {
+                callback.accept(mLruProcesses.get(i));
+            }
+        } else {
+            for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+                callback.accept(mLruProcesses.get(i));
+            }
+        }
+    }
+
+    /**
+     * Search in the LRU list, invoke the given {@code callback} with each of the ProcessRecord
+     * in that list; if the callback returns a non-null object, halt the search, return that
+     * object as the return value of this search function.
+     *
+     * @param iterateForward If {@code true}, iterate the LRU list from the least recent used
+     *                       to most recent used ProcessRecord.
+     * @param callback The callback interface to accept the current ProcessRecord; if it returns
+     *                 a non-null object, the search will be halted and this object will be used
+     *                 as the return value of this search function.
+     */
+    @GuardedBy(anyOf = {"mService", "mProfileLock"})
+    <R> R searchEachLruProcessesLOSP(boolean iterateForward,
+            @NonNull Function<ProcessRecord, R> callback) {
+        if (iterateForward) {
+            for (int i = 0, size = mLruProcesses.size(); i < size; i++) {
+                R r;
+                if ((r = callback.apply(mLruProcesses.get(i))) != null) {
+                    return r;
+                }
+            }
+        } else {
+            for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+                R r;
+                if ((r = callback.apply(mLruProcesses.get(i))) != null) {
+                    return r;
+                }
+            }
+        }
+        return null;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProfileLock"})
+    boolean isInLruListLOSP(ProcessRecord app) {
+        return mLruProcesses.contains(app);
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProfileLock"})
+    int getLruSeqLOSP() {
+        return mLruSeq;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProfileLock"})
+    MyProcessMap getProcessNamesLOSP() {
+        return mProcessNames;
+    }
+
     @GuardedBy("mService")
     void dumpLruListHeaderLocked(PrintWriter pw) {
         pw.print("  Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
@@ -3673,7 +3891,8 @@
         pw.println("):");
     }
 
-    void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc, String prefix) {
+    @GuardedBy("mService")
+    private void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc, String prefix) {
         pw.print(prefix);
         pw.print('#');
         if (index < 10) {
@@ -3681,15 +3900,16 @@
         }
         pw.print(index);
         pw.print(": ");
-        pw.print(makeOomAdjString(proc.setAdj, false));
+        pw.print(makeOomAdjString(proc.mState.getSetAdj(), false));
         pw.print(' ');
-        pw.print(makeProcStateString(proc.getCurProcState()));
+        pw.print(makeProcStateString(proc.mState.getCurProcState()));
         pw.print(' ');
-        ActivityManager.printCapabilitiesSummary(pw, proc.curCapability);
+        ActivityManager.printCapabilitiesSummary(pw, proc.mState.getCurCapability());
         pw.print(' ');
         pw.print(proc.toShortString());
-        if (proc.hasActivitiesOrRecentTasks() || proc.hasClientActivities()
-                || proc.treatLikeActivity) {
+        final ProcessServiceRecord psr = proc.mServices;
+        if (proc.hasActivitiesOrRecentTasks() || psr.hasClientActivities()
+                || psr.isTreatedLikeActivity()) {
             pw.print(" act:");
             boolean printed = false;
             if (proc.hasActivities()) {
@@ -3703,14 +3923,14 @@
                 pw.print("recents");
                 printed = true;
             }
-            if (proc.hasClientActivities()) {
+            if (psr.hasClientActivities()) {
                 if (printed) {
                     pw.print("|");
                 }
                 pw.print("client");
                 printed = true;
             }
-            if (proc.treatLikeActivity) {
+            if (psr.isTreatedLikeActivity()) {
                 if (printed) {
                     pw.print("|");
                 }
@@ -3720,6 +3940,7 @@
         pw.println();
     }
 
+    @GuardedBy("mService")
     boolean dumpLruLocked(PrintWriter pw, String dumpPackage, String prefix) {
         final int lruSize = mLruProcesses.size();
         final String innerPrefix;
@@ -3786,8 +4007,8 @@
         return true;
     }
 
-    @GuardedBy("mService")
-    void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+    @GuardedBy({"mService", "mProcLock"})
+    void dumpProcessesLSP(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
         boolean needSep = false;
         int numPers = 0;
@@ -3861,7 +4082,7 @@
             needSep = true;
         }
 
-        if (getLruSizeLocked() > 0) {
+        if (getLruSizeLOSP() > 0) {
             if (needSep) {
                 pw.println();
             }
@@ -3871,12 +4092,12 @@
             needSep = true;
         }
 
-        mService.dumpOtherProcessesInfoLocked(fd, pw, dumpAll, dumpPackage, dumpAppId, numPers,
+        mService.dumpOtherProcessesInfoLSP(fd, pw, dumpAll, dumpPackage, dumpAppId, numPers,
                 needSep);
     }
 
-    @GuardedBy("this")
-    void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
+    @GuardedBy({"mService", "mProcLock"})
+    void writeProcessesToProtoLSP(ProtoOutputStream proto, String dumpPackage) {
         int numPers = 0;
 
         final int numOfNames = mProcessNames.getMap().size();
@@ -3910,9 +4131,9 @@
         mActiveUids.dumpProto(proto, dumpPackage, dumpAppId,
                 ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
 
-        if (getLruSizeLocked() > 0) {
+        if (getLruSizeLOSP() > 0) {
             long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
-            int total = getLruSizeLocked();
+            int total = getLruSizeLOSP();
             proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total);
             proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT,
                     total - mLruProcessActivityStart);
@@ -3924,7 +4145,7 @@
             proto.end(lruToken);
         }
 
-        mService.writeOtherProcessesInfoToProtoLocked(proto, dumpPackage, dumpAppId, numPers);
+        mService.writeOtherProcessesInfoToProtoLSP(proto, dumpPackage, dumpAppId, numPers);
     }
 
     private static ArrayList<Pair<ProcessRecord, Integer>> sortProcessOomList(
@@ -3944,14 +4165,18 @@
             @Override
             public int compare(Pair<ProcessRecord, Integer> object1,
                     Pair<ProcessRecord, Integer> object2) {
-                if (object1.first.setAdj != object2.first.setAdj) {
-                    return object1.first.setAdj > object2.first.setAdj ? -1 : 1;
+                final int adj = object2.first.mState.getSetAdj() - object1.first.mState.getSetAdj();
+                if (adj != 0) {
+                    return adj;
                 }
-                if (object1.first.setProcState != object2.first.setProcState) {
-                    return object1.first.setProcState > object2.first.setProcState ? -1 : 1;
+                final int procState = object2.first.mState.getSetProcState()
+                        - object1.first.mState.getSetProcState();
+                if (procState != 0) {
+                    return procState;
                 }
-                if (object1.second.intValue() != object2.second.intValue()) {
-                    return object1.second.intValue() > object2.second.intValue() ? -1 : 1;
+                final int val = object2.second - object1.second;
+                if (val != 0) {
+                    return val;
                 }
                 return 0;
             }
@@ -3971,13 +4196,15 @@
 
         for (int i = list.size() - 1; i >= 0; i--) {
             ProcessRecord r = list.get(i).first;
+            final ProcessStateRecord state = r.mState;
+            final ProcessServiceRecord psr = r.mServices;
             long token = proto.start(fieldId);
-            String oomAdj = makeOomAdjString(r.setAdj, true);
+            String oomAdj = makeOomAdjString(state.getSetAdj(), true);
             proto.write(ProcessOomProto.PERSISTENT, r.isPersistent());
             proto.write(ProcessOomProto.NUM, (origList.size() - 1) - list.get(i).second);
             proto.write(ProcessOomProto.OOM_ADJ, oomAdj);
             int schedGroup = ProcessOomProto.SCHED_GROUP_UNKNOWN;
-            switch (r.setSchedGroup) {
+            switch (state.getSetSchedGroup()) {
                 case SCHED_GROUP_BACKGROUND:
                     schedGroup = ProcessOomProto.SCHED_GROUP_BACKGROUND;
                     break;
@@ -3994,52 +4221,52 @@
             if (schedGroup != ProcessOomProto.SCHED_GROUP_UNKNOWN) {
                 proto.write(ProcessOomProto.SCHED_GROUP, schedGroup);
             }
-            if (r.hasForegroundActivities()) {
+            if (state.hasForegroundActivities()) {
                 proto.write(ProcessOomProto.ACTIVITIES, true);
-            } else if (r.hasForegroundServices()) {
+            } else if (psr.hasForegroundServices()) {
                 proto.write(ProcessOomProto.SERVICES, true);
             }
             proto.write(ProcessOomProto.STATE,
-                    makeProcStateProtoEnum(r.getCurProcState()));
+                    makeProcStateProtoEnum(state.getCurProcState()));
             proto.write(ProcessOomProto.TRIM_MEMORY_LEVEL, r.mProfile.getTrimMemoryLevel());
             r.dumpDebug(proto, ProcessOomProto.PROC);
-            proto.write(ProcessOomProto.ADJ_TYPE, r.adjType);
-            if (r.adjSource != null || r.adjTarget != null) {
-                if (r.adjTarget instanceof  ComponentName) {
-                    ComponentName cn = (ComponentName) r.adjTarget;
+            proto.write(ProcessOomProto.ADJ_TYPE, state.getAdjType());
+            if (state.getAdjSource() != null || state.getAdjTarget() != null) {
+                if (state.getAdjTarget() instanceof  ComponentName) {
+                    ComponentName cn = (ComponentName) state.getAdjTarget();
                     cn.dumpDebug(proto, ProcessOomProto.ADJ_TARGET_COMPONENT_NAME);
-                } else if (r.adjTarget != null) {
-                    proto.write(ProcessOomProto.ADJ_TARGET_OBJECT, r.adjTarget.toString());
+                } else if (state.getAdjTarget() != null) {
+                    proto.write(ProcessOomProto.ADJ_TARGET_OBJECT, state.getAdjTarget().toString());
                 }
-                if (r.adjSource instanceof ProcessRecord) {
-                    ProcessRecord p = (ProcessRecord) r.adjSource;
+                if (state.getAdjSource() instanceof ProcessRecord) {
+                    ProcessRecord p = (ProcessRecord) state.getAdjSource();
                     p.dumpDebug(proto, ProcessOomProto.ADJ_SOURCE_PROC);
-                } else if (r.adjSource != null) {
-                    proto.write(ProcessOomProto.ADJ_SOURCE_OBJECT, r.adjSource.toString());
+                } else if (state.getAdjSource() != null) {
+                    proto.write(ProcessOomProto.ADJ_SOURCE_OBJECT, state.getAdjSource().toString());
                 }
             }
             if (inclDetails) {
                 long detailToken = proto.start(ProcessOomProto.DETAIL);
-                proto.write(ProcessOomProto.Detail.MAX_ADJ, r.maxAdj);
-                proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, r.getCurRawAdj());
-                proto.write(ProcessOomProto.Detail.SET_RAW_ADJ, r.setRawAdj);
-                proto.write(ProcessOomProto.Detail.CUR_ADJ, r.curAdj);
-                proto.write(ProcessOomProto.Detail.SET_ADJ, r.setAdj);
+                proto.write(ProcessOomProto.Detail.MAX_ADJ, state.getMaxAdj());
+                proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, state.getCurRawAdj());
+                proto.write(ProcessOomProto.Detail.SET_RAW_ADJ, state.getSetRawAdj());
+                proto.write(ProcessOomProto.Detail.CUR_ADJ, state.getCurAdj());
+                proto.write(ProcessOomProto.Detail.SET_ADJ, state.getSetAdj());
                 proto.write(ProcessOomProto.Detail.CURRENT_STATE,
-                        makeProcStateProtoEnum(r.getCurProcState()));
+                        makeProcStateProtoEnum(state.getCurProcState()));
                 proto.write(ProcessOomProto.Detail.SET_STATE,
-                        makeProcStateProtoEnum(r.setProcState));
+                        makeProcStateProtoEnum(state.getSetProcState()));
                 proto.write(ProcessOomProto.Detail.LAST_PSS, DebugUtils.sizeValueToString(
                         r.mProfile.getLastPss() * 1024, new StringBuilder()));
                 proto.write(ProcessOomProto.Detail.LAST_SWAP_PSS, DebugUtils.sizeValueToString(
                         r.mProfile.getLastSwapPss() * 1024, new StringBuilder()));
                 proto.write(ProcessOomProto.Detail.LAST_CACHED_PSS, DebugUtils.sizeValueToString(
                         r.mProfile.getLastCachedPss() * 1024, new StringBuilder()));
-                proto.write(ProcessOomProto.Detail.CACHED, r.isCached());
-                proto.write(ProcessOomProto.Detail.EMPTY, r.empty);
-                proto.write(ProcessOomProto.Detail.HAS_ABOVE_CLIENT, r.hasAboveClient);
+                proto.write(ProcessOomProto.Detail.CACHED, state.isCached());
+                proto.write(ProcessOomProto.Detail.EMPTY, state.isEmpty());
+                proto.write(ProcessOomProto.Detail.HAS_ABOVE_CLIENT, psr.hasAboveClient());
 
-                if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
+                if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
                     long lastCpuTime = r.mProfile.mLastCpuTime.get();
                     if (lastCpuTime != 0) {
                         long uptimeSince = curUptime - service.mLastPowerCheckUptime;
@@ -4073,9 +4300,11 @@
 
         for (int i = list.size() - 1; i >= 0; i--) {
             ProcessRecord r = list.get(i).first;
-            String oomAdj = makeOomAdjString(r.setAdj, false);
+            final ProcessStateRecord state = r.mState;
+            final ProcessServiceRecord psr = r.mServices;
+            String oomAdj = makeOomAdjString(state.getSetAdj(), false);
             char schedGroup;
-            switch (r.setSchedGroup) {
+            switch (state.getSetSchedGroup()) {
                 case SCHED_GROUP_BACKGROUND:
                     schedGroup = 'b';
                     break;
@@ -4096,14 +4325,14 @@
                     break;
             }
             char foreground;
-            if (r.hasForegroundActivities()) {
+            if (state.hasForegroundActivities()) {
                 foreground = 'A';
-            } else if (r.hasForegroundServices()) {
+            } else if (psr.hasForegroundServices()) {
                 foreground = 'S';
             } else {
                 foreground = ' ';
             }
-            String procState = makeProcStateString(r.getCurProcState());
+            String procState = makeProcStateString(state.getCurProcState());
             pw.print(prefix);
             pw.print(r.isPersistent() ? persistentLabel : normalLabel);
             pw.print(" #");
@@ -4119,7 +4348,7 @@
             pw.print('/');
             pw.print(procState);
             pw.print(' ');
-            ActivityManager.printCapabilitiesSummary(pw, r.curCapability);
+            ActivityManager.printCapabilitiesSummary(pw, state.getCurCapability());
             pw.print(' ');
             pw.print(" t:");
             if (r.mProfile.getTrimMemoryLevel() < 10) pw.print(' ');
@@ -4127,25 +4356,25 @@
             pw.print(' ');
             pw.print(r.toShortString());
             pw.print(" (");
-            pw.print(r.adjType);
+            pw.print(state.getAdjType());
             pw.println(')');
-            if (r.adjSource != null || r.adjTarget != null) {
+            if (state.getAdjSource() != null || state.getAdjTarget() != null) {
                 pw.print(prefix);
                 pw.print("    ");
-                if (r.adjTarget instanceof ComponentName) {
-                    pw.print(((ComponentName) r.adjTarget).flattenToShortString());
-                } else if (r.adjTarget != null) {
-                    pw.print(r.adjTarget.toString());
+                if (state.getAdjTarget() instanceof ComponentName) {
+                    pw.print(((ComponentName) state.getAdjTarget()).flattenToShortString());
+                } else if (state.getAdjTarget() != null) {
+                    pw.print(state.getAdjTarget().toString());
                 } else {
                     pw.print("{null}");
                 }
                 pw.print("<=");
-                if (r.adjSource instanceof ProcessRecord) {
+                if (state.getAdjSource() instanceof ProcessRecord) {
                     pw.print("Proc{");
-                    pw.print(((ProcessRecord) r.adjSource).toShortString());
+                    pw.print(((ProcessRecord) state.getAdjSource()).toShortString());
                     pw.println("}");
-                } else if (r.adjSource != null) {
-                    pw.println(r.adjSource.toString());
+                } else if (state.getAdjSource() != null) {
+                    pw.println(state.getAdjSource().toString());
                 } else {
                     pw.println("{null}");
                 }
@@ -4153,16 +4382,15 @@
             if (inclDetails) {
                 pw.print(prefix);
                 pw.print("    ");
-                pw.print("oom: max="); pw.print(r.maxAdj);
-                pw.print(" curRaw="); pw.print(r.getCurRawAdj());
-                pw.print(" setRaw="); pw.print(r.setRawAdj);
-                pw.print(" cur="); pw.print(r.curAdj);
-                pw.print(" set="); pw.println(r.setAdj);
+                pw.print("oom: max="); pw.print(state.getMaxAdj());
+                pw.print(" curRaw="); pw.print(state.getCurRawAdj());
+                pw.print(" setRaw="); pw.print(state.getSetRawAdj());
+                pw.print(" cur="); pw.print(state.getCurAdj());
+                pw.print(" set="); pw.println(state.getSetAdj());
                 pw.print(prefix);
                 pw.print("    ");
-                pw.print("state: cur="); pw.print(
-                        makeProcStateString(r.getCurProcState()));
-                pw.print(" set="); pw.print(makeProcStateString(r.setProcState));
+                pw.print("state: cur="); pw.print(makeProcStateString(state.getCurProcState()));
+                pw.print(" set="); pw.print(makeProcStateString(state.getSetProcState()));
                 pw.print(" lastPss=");
                 DebugUtils.printSizeValue(pw, r.mProfile.getLastPss() * 1024);
                 pw.print(" lastSwapPss=");
@@ -4172,11 +4400,11 @@
                 pw.println();
                 pw.print(prefix);
                 pw.print("    ");
-                pw.print("cached="); pw.print(r.isCached());
-                pw.print(" empty="); pw.print(r.empty);
-                pw.print(" hasAboveClient="); pw.println(r.hasAboveClient);
+                pw.print("cached="); pw.print(state.isCached());
+                pw.print(" empty="); pw.print(state.isEmpty());
+                pw.print(" hasAboveClient="); pw.println(psr.hasAboveClient());
 
-                if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
+                if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
                     long lastCpuTime = r.mProfile.mLastCpuTime.get();
                     if (lastCpuTime != 0) {
                         long timeUsed = r.mProfile.mCurCpuTime.get() - lastCpuTime;
@@ -4212,9 +4440,10 @@
         pw.println(")");
     }
 
+    @GuardedBy("mService")
     boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String[] args,
             int opti, boolean dumpAll, String dumpPackage, boolean inclGc) {
-        if (getLruSizeLocked() > 0) {
+        if (getLruSizeLOSP() > 0) {
             if (needSep) pw.println();
             needSep = true;
             pw.println("  OOM levels:");
@@ -4235,14 +4464,14 @@
             printOomLevel(pw, "CACHED_APP_MAX_ADJ", CACHED_APP_MAX_ADJ);
 
             if (needSep) pw.println();
-            pw.print("  Process OOM control ("); pw.print(getLruSizeLocked());
+            pw.print("  Process OOM control ("); pw.print(getLruSizeLOSP());
             pw.print(" total, non-act at ");
-            pw.print(getLruSizeLocked() - mLruProcessActivityStart);
+            pw.print(getLruSizeLOSP() - mLruProcessActivityStart);
             pw.print(", non-svc at ");
-            pw.print(getLruSizeLocked() - mLruProcessServiceStart);
+            pw.print(getLruSizeLOSP() - mLruProcessServiceStart);
             pw.println("):");
-            dumpProcessOomList(pw, mService, mLruProcesses, "    ", "Proc", "PERS", true,
-                    dumpPackage);
+            dumpProcessOomList(pw, mService, mLruProcesses,
+                    "    ", "Proc", "PERS", true, dumpPackage);
             needSep = true;
         }
 
@@ -4396,8 +4625,8 @@
         mProcessObservers.finishBroadcast();
     }
 
-    @GuardedBy("mService")
-    ArrayList<ProcessRecord> collectProcessesLocked(int start, boolean allPkgs, String[] args) {
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    ArrayList<ProcessRecord> collectProcessesLOSP(int start, boolean allPkgs, String[] args) {
         ArrayList<ProcessRecord> procs;
         if (args != null && args.length > start
                 && args[start].charAt(0) != '-') {
@@ -4409,7 +4638,7 @@
             }
             for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
                 ProcessRecord proc = mLruProcesses.get(i);
-                if (proc.pid > 0 && proc.pid == pid) {
+                if (proc.getPid() > 0 && proc.getPid() == pid) {
                     procs.add(proc);
                 } else if (allPkgs && proc.getPkgList() != null
                         && proc.getPkgList().containsKey(args[start])) {
@@ -4427,12 +4656,12 @@
         return procs;
     }
 
-    @GuardedBy("mService")
-    void updateApplicationInfoLocked(List<String> packagesToUpdate, int userId,
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    void updateApplicationInfoLOSP(List<String> packagesToUpdate, int userId,
             boolean updateFrameworkRes) {
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             final ProcessRecord app = mLruProcesses.get(i);
-            if (app.thread == null) {
+            if (app.getThread() == null) {
                 continue;
             }
 
@@ -4446,14 +4675,14 @@
                         final ApplicationInfo ai = AppGlobals.getPackageManager()
                                 .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
                         if (ai != null) {
-                            app.thread.scheduleApplicationInfoChanged(ai);
+                            app.getThread().scheduleApplicationInfoChanged(ai);
                             if (ai.packageName.equals(app.info.packageName)) {
                                 app.info = ai;
                             }
                         }
                     } catch (RemoteException e) {
                         Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
-                                packageName, app));
+                                    packageName, app));
                     }
                 }
             });
@@ -4465,14 +4694,15 @@
         boolean foundProcess = false;
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord r = mLruProcesses.get(i);
-            if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
+            final IApplicationThread thread = r.getThread();
+            if (thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
                 try {
                     for (int index = packages.length - 1; index >= 0 && !foundProcess; index--) {
                         if (packages[index].equals(r.info.packageName)) {
                             foundProcess = true;
                         }
                     }
-                    r.thread.dispatchPackageBroadcast(cmd, packages);
+                    thread.dispatchPackageBroadcast(cmd, packages);
                 } catch (RemoteException ex) {
                 }
             }
@@ -4487,15 +4717,15 @@
     }
 
     /** Returns the uid's process state or PROCESS_STATE_NONEXISTENT if not running */
-    @GuardedBy("mService")
-    int getUidProcStateLocked(int uid) {
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getUidProcStateLOSP(int uid) {
         UidRecord uidRec = mActiveUids.get(uid);
         return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState();
     }
 
     /** Returns the UidRecord for the given uid, if it exists. */
-    @GuardedBy("mService")
-    UidRecord getUidRecordLocked(int uid) {
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    UidRecord getUidRecordLOSP(int uid) {
         return mActiveUids.get(uid);
     }
 
@@ -4512,10 +4742,10 @@
                 continue;
             }
             final UidRecord uidRec = mActiveUids.valueAt(i);
-            if (!uidRec.idle) {
+            if (!uidRec.isIdle()) {
                 continue;
             }
-            mService.doStopUidLocked(uidRec.uid, uidRec);
+            mService.doStopUidLocked(uidRec.getUid(), uidRec);
         }
     }
 
@@ -4529,14 +4759,16 @@
      *         {@link #NETWORK_STATE_NO_CHANGE}.
      */
     @VisibleForTesting
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
     int getBlockStateForUid(UidRecord uidRec) {
         // Denotes whether uid's process state is currently allowed network access.
         final boolean isAllowed =
                 isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState())
                 || isProcStateAllowedWhileOnRestrictBackground(uidRec.getCurProcState());
         // Denotes whether uid's process state was previously allowed network access.
-        final boolean wasAllowed = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.setProcState)
-                || isProcStateAllowedWhileOnRestrictBackground(uidRec.setProcState);
+        final boolean wasAllowed =
+                isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getSetProcState())
+                || isProcStateAllowedWhileOnRestrictBackground(uidRec.getSetProcState());
 
         // When the uid is coming to foreground, AMS should inform the app thread that it should
         // block for the network rules to get updated before launching an activity.
@@ -4557,8 +4789,8 @@
      * {@link ProcessList#mProcStateSeqCounter} and notifies the app if it needs to block.
      */
     @VisibleForTesting
-    @GuardedBy("mService")
-    void incrementProcStateSeqAndNotifyAppsLocked(ActiveUids activeUids) {
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    void incrementProcStateSeqAndNotifyAppsLOSP(ActiveUids activeUids) {
         if (mService.mWaitForNetworkTimeoutMs <= 0) {
             return;
         }
@@ -4567,14 +4799,14 @@
         for (int i = activeUids.size() - 1; i >= 0; --i) {
             final UidRecord uidRec = activeUids.valueAt(i);
             // If the network is not restricted for uid, then nothing to do here.
-            if (!mService.mInjector.isNetworkRestrictedForUid(uidRec.uid)) {
+            if (!mService.mInjector.isNetworkRestrictedForUid(uidRec.getUid())) {
                 continue;
             }
-            if (!UserHandle.isApp(uidRec.uid) || !uidRec.hasInternetPermission) {
+            if (!UserHandle.isApp(uidRec.getUid()) || !uidRec.hasInternetPermission) {
                 continue;
             }
             // If process state is not changed, then there's nothing to do.
-            if (uidRec.setProcState == uidRec.getCurProcState()) {
+            if (uidRec.getSetProcState() == uidRec.getCurProcState()) {
                 continue;
             }
             final int blockState = getBlockStateForUid(uidRec);
@@ -4589,7 +4821,7 @@
                     if (blockingUids == null) {
                         blockingUids = new ArrayList<>();
                     }
-                    blockingUids.add(uidRec.uid);
+                    blockingUids.add(uidRec.getUid());
                 } else {
                     if (DEBUG_NETWORK) {
                         Slog.d(TAG_NETWORK, "uid going to background, notifying all blocking"
@@ -4612,15 +4844,16 @@
             if (!blockingUids.contains(app.uid)) {
                 continue;
             }
-            if (!app.killedByAm && app.thread != null) {
-                final UidRecord uidRec = getUidRecordLocked(app.uid);
+            final IApplicationThread thread = app.getThread();
+            if (!app.isKilledByAm() && thread != null) {
+                final UidRecord uidRec = getUidRecordLOSP(app.uid);
                 try {
                     if (DEBUG_NETWORK) {
                         Slog.d(TAG_NETWORK, "Informing app thread that it needs to block: "
                                 + uidRec);
                     }
                     if (uidRec != null) {
-                        app.thread.setNetworkBlockSeq(uidRec.curProcStateSeq);
+                        thread.setNetworkBlockSeq(uidRec.curProcStateSeq);
                     }
                 } catch (RemoteException ignored) {
                 }
@@ -4692,7 +4925,7 @@
             Slog.i(TAG, "note: " + app + " died, saving the exit info");
         }
 
-        Watchdog.getInstance().processDied(app.processName, app.pid);
+        Watchdog.getInstance().processDied(app.processName, app.getPid());
         mAppExitInfoTracker.scheduleNoteProcessDied(app);
     }
 
@@ -4814,10 +5047,10 @@
             }
 
             final Bundle bundle = new Bundle();
-            bundle.putInt(EXTRA_PID, app.pid);
+            bundle.putInt(EXTRA_PID, app.getPid());
             bundle.putInt(EXTRA_UID, app.uid);
             // Since the pid could be reused, let's get the actual start time of each process
-            bundle.putLong(EXTRA_TIMESTAMP, app.startTime);
+            bundle.putLong(EXTRA_TIMESTAMP, app.getStartTime());
             bundle.putString(EXTRA_REASON, reason);
             bundle.putInt(EXTRA_REQUESTER, requester);
             List<Bundle> list = mWorkItems.get(app.uid);
@@ -4909,13 +5142,14 @@
                 app = mService.mPidsSelfLocked.get(pid);
             }
 
-            if (app == null || app.pid != pid || app.uid != uid || app.startTime != timestamp) {
+            if (app == null || app.getPid() != pid || app.uid != uid
+                    || app.getStartTime() != timestamp) {
                 // This process record has been reused for another process, meaning the old process
                 // has been gone.
                 return true;
             }
 
-            if (app.getPkgList().forEachPackage(pkgName -> {
+            if (app.getPkgList().searchEachPackage(pkgName -> {
                 if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.contains(pkgName)) {
                     // One of the packages in this process is exempted
                     return Boolean.TRUE;
@@ -4926,12 +5160,12 @@
             }
 
             if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.contains(
-                    app.getReportedProcState())) {
+                    app.mState.getReportedProcState())) {
                 // We need to reschedule it.
                 return false;
             }
 
-            app.kill(reason, ApplicationExitInfo.REASON_OTHER,
+            app.killLocked(reason, ApplicationExitInfo.REASON_OTHER,
                     ApplicationExitInfo.SUBREASON_IMPERCEPTIBLE, true);
 
             if (!app.isolated) {
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index 9fd2bd7..e533cc3 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -26,6 +26,7 @@
 import android.util.DebugUtils;
 import android.util.TimeUtils;
 
+import com.android.internal.annotations.CompositeRWLock;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.procstats.ProcessState;
 import com.android.internal.app.procstats.ProcessStats;
@@ -141,13 +142,13 @@
     /**
      * Last selected memory trimming level.
      */
-    @GuardedBy("mService")
+    @CompositeRWLock({"mService", "mProcLock"})
     private int mTrimMemoryLevel;
 
     /**
      * Want to clean up resources from showing UI?
      */
-    @GuardedBy("mService")
+    @GuardedBy("mProcLock")
     private boolean mPendingUiClean;
 
     /**
@@ -164,7 +165,7 @@
     /**
      * When we last told the app that memory is low.
      */
-    @GuardedBy("mService")
+    @CompositeRWLock({"mService", "mProfilerLock"})
     private long mLastLowMemory;
 
     /**
@@ -193,9 +194,12 @@
     @GuardedBy("mProfilerLock")
     private long mLastStateTime;
 
+    private final ActivityManagerGlobalLock mProcLock;
+
     ProcessProfileRecord(final ProcessRecord app) {
         mApp = app;
         mService = app.mService;
+        mProcLock = mService.mProcLock;
         mProfilerLock = mService.mAppProfiler.mProfilerLock;
     }
 
@@ -410,22 +414,22 @@
         mPssStatType = pssStatType;
     }
 
-    @GuardedBy("mService")
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
     int getTrimMemoryLevel() {
         return mTrimMemoryLevel;
     }
 
-    @GuardedBy("mService")
+    @GuardedBy({"mService", "mProcLock"})
     void setTrimMemoryLevel(int trimMemoryLevel) {
         mTrimMemoryLevel = trimMemoryLevel;
     }
 
-    @GuardedBy("mService")
+    @GuardedBy("mProcLock")
     boolean hasPendingUiClean() {
         return mPendingUiClean;
     }
 
-    @GuardedBy("mService")
+    @GuardedBy("mProcLock")
     void setPendingUiClean(boolean pendingUiClean) {
         mPendingUiClean = pendingUiClean;
         mApp.getWindowProcessController().setPendingUiClean(pendingUiClean);
@@ -449,12 +453,12 @@
         mLastRequestedGc = lastRequestedGc;
     }
 
-    @GuardedBy("mService")
+    @GuardedBy(anyOf = {"mService", "mProfilerLock"})
     long getLastLowMemory() {
         return mLastLowMemory;
     }
 
-    @GuardedBy("mService")
+    @GuardedBy({"mService", "mProfilerLock"})
     void setLastLowMemory(long lastLowMemory) {
         mLastLowMemory = lastLowMemory;
     }
@@ -593,11 +597,11 @@
     }
 
     @GuardedBy({"mService", "mProfilerLock"})
-    void updateProcState(ProcessRecord app) {
-        mSetProcState = app.getCurProcState();
-        mSetAdj = app.curAdj;
-        mCurRawAdj = app.getCurRawAdj();
-        mLastStateTime = app.lastStateTime;
+    void updateProcState(ProcessStateRecord state) {
+        mSetProcState = state.getCurProcState();
+        mSetAdj = state.getCurAdj();
+        mCurRawAdj = state.getCurRawAdj();
+        mLastStateTime = state.getLastStateTime();
     }
 
     @GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/ProcessProviderRecord.java b/services/core/java/com/android/server/am/ProcessProviderRecord.java
new file mode 100644
index 0000000..751e8a82
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessProviderRecord.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2020 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 android.util.ArrayMap;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * The state info of all content providers in the process.
+ */
+final class ProcessProviderRecord {
+    final ProcessRecord mApp;
+    private final ActivityManagerService mService;
+
+    /**
+     * The last time someone else was using a provider in this process.
+     */
+    private long mLastProviderTime;
+
+    /**
+     * class (String) -> ContentProviderRecord.
+     */
+    private final ArrayMap<String, ContentProviderRecord> mPubProviders = new ArrayMap<>();
+
+    /**
+     * All ContentProviderRecord process is using.
+     */
+    private final ArrayList<ContentProviderConnection> mConProviders = new ArrayList<>();
+
+    long getLastProviderTime() {
+        return mLastProviderTime;
+    }
+
+    void setLastProviderTime(long lastProviderTime) {
+        mLastProviderTime = lastProviderTime;
+    }
+
+    boolean hasProvider(String name) {
+        return mPubProviders.containsKey(name);
+    }
+
+    ContentProviderRecord getProvider(String name) {
+        return mPubProviders.get(name);
+    }
+
+    int numberOfProviders() {
+        return mPubProviders.size();
+    }
+
+    ContentProviderRecord getProviderAt(int index) {
+        return mPubProviders.valueAt(index);
+    }
+
+    void installProvider(String name, ContentProviderRecord provider) {
+        mPubProviders.put(name, provider);
+    }
+
+    void removeProvider(String name) {
+        mPubProviders.remove(name);
+    }
+
+    void ensureProviderCapacity(int capacity) {
+        mPubProviders.ensureCapacity(capacity);
+    }
+
+    int numberOfProviderConnections() {
+        return mConProviders.size();
+    }
+
+    ContentProviderConnection getProviderConnectionAt(int index) {
+        return mConProviders.get(index);
+    }
+
+    void addProviderConnection(ContentProviderConnection connection) {
+        mConProviders.add(connection);
+    }
+
+    boolean removeProviderConnection(ContentProviderConnection connection) {
+        return mConProviders.remove(connection);
+    }
+
+    ProcessProviderRecord(ProcessRecord app) {
+        mApp = app;
+        mService = app.mService;
+    }
+
+    /**
+     * @return Should the process restart or not.
+     */
+    @GuardedBy("mService")
+    boolean onCleanupApplicationRecordLocked(boolean allowRestart) {
+        boolean restart = false;
+        // Remove published content providers.
+        for (int i = mPubProviders.size() - 1; i >= 0; i--) {
+            final ContentProviderRecord cpr = mPubProviders.valueAt(i);
+            if (cpr.proc != mApp) {
+                // If the hosting process record isn't really us, bail out
+                continue;
+            }
+            final boolean alwaysRemove = mApp.mErrorState.isBad() || !allowRestart;
+            final boolean inLaunching = mService.mCpHelper
+                    .removeDyingProviderLocked(mApp, cpr, alwaysRemove);
+            if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {
+                // We left the provider in the launching list, need to
+                // restart it.
+                restart = true;
+            }
+
+            cpr.provider = null;
+            cpr.setProcess(null);
+        }
+        mPubProviders.clear();
+
+        // Take care of any launching providers waiting for this process.
+        if (mService.mCpHelper.cleanupAppInLaunchingProvidersLocked(mApp, false)) {
+            mService.mProcessList.noteProcessDiedLocked(mApp);
+            restart = true;
+        }
+
+        // Unregister from connected content providers.
+        if (!mConProviders.isEmpty()) {
+            for (int i = mConProviders.size() - 1; i >= 0; i--) {
+                final ContentProviderConnection conn = mConProviders.get(i);
+                conn.provider.connections.remove(conn);
+                mService.stopAssociationLocked(mApp.uid, mApp.processName, conn.provider.uid,
+                        conn.provider.appInfo.longVersionCode, conn.provider.name,
+                        conn.provider.info.processName);
+            }
+            mConProviders.clear();
+        }
+
+        return restart;
+    }
+
+    void dump(PrintWriter pw, String prefix, long nowUptime) {
+        if (mLastProviderTime > 0) {
+            pw.print(prefix); pw.print("lastProviderTime=");
+            TimeUtils.formatDuration(mLastProviderTime, nowUptime, pw);
+            pw.println();
+        }
+        if (mPubProviders.size() > 0) {
+            pw.print(prefix); pw.println("Published Providers:");
+            for (int i = 0, size = mPubProviders.size(); i < size; i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(mPubProviders.keyAt(i));
+                pw.print(prefix); pw.print("    -> "); pw.println(mPubProviders.valueAt(i));
+            }
+        }
+        if (mConProviders.size() > 0) {
+            pw.print(prefix); pw.println("Connected Providers:");
+            for (int i = 0, size = mConProviders.size(); i < size; i++) {
+                pw.print(prefix); pw.print("  - ");
+                pw.println(mConProviders.get(i).toShortString());
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ProcessReceiverRecord.java b/services/core/java/com/android/server/am/ProcessReceiverRecord.java
new file mode 100644
index 0000000..8d3e9669
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessReceiverRecord.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 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 android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+
+/**
+ * The state info of all broadcast receivers in the process.
+ */
+final class ProcessReceiverRecord {
+    final ProcessRecord mApp;
+    private final ActivityManagerService mService;
+
+    /**
+     * mReceivers currently running in the app.
+     */
+    private final ArraySet<BroadcastRecord> mCurReceivers = new ArraySet<BroadcastRecord>();
+
+    /**
+     * All IIntentReceivers that are registered from this process.
+     */
+    private final ArraySet<ReceiverList> mReceivers = new ArraySet<>();
+
+    int numberOfCurReceivers() {
+        return mCurReceivers.size();
+    }
+
+    BroadcastRecord getCurReceiverAt(int index) {
+        return mCurReceivers.valueAt(index);
+    }
+
+    boolean hasCurReceiver(BroadcastRecord receiver) {
+        return mCurReceivers.contains(receiver);
+    }
+
+    void addCurReceiver(BroadcastRecord receiver) {
+        mCurReceivers.add(receiver);
+    }
+
+    void removeCurReceiver(BroadcastRecord receiver) {
+        mCurReceivers.remove(receiver);
+    }
+
+    int numberOfReceivers() {
+        return mReceivers.size();
+    }
+
+    ReceiverList getReceiverAt(int index) {
+        return mReceivers.valueAt(index);
+    }
+
+    void addReceiver(ReceiverList receiver) {
+        mReceivers.add(receiver);
+    }
+
+    void removeReceiver(ReceiverList receiver) {
+        mReceivers.remove(receiver);
+    }
+
+    ProcessReceiverRecord(ProcessRecord app) {
+        mApp = app;
+        mService = app.mService;
+    }
+
+    @GuardedBy("mService")
+    void onCleanupApplicationRecordLocked() {
+        // Unregister any mReceivers.
+        for (int i = mReceivers.size() - 1; i >= 0; i--) {
+            mService.removeReceiverLocked(mReceivers.valueAt(i));
+        }
+        mReceivers.clear();
+    }
+
+    void dump(PrintWriter pw, String prefix, long nowUptime) {
+        if (!mCurReceivers.isEmpty()) {
+            pw.print(prefix); pw.println("Current mReceivers:");
+            for (int i = 0, size = mCurReceivers.size(); i < size; i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(mCurReceivers.valueAt(i));
+            }
+        }
+        if (mReceivers.size() > 0) {
+            pw.print(prefix); pw.println("mReceivers:");
+            for (int i = 0, size = mReceivers.size(); i < size; i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(mReceivers.valueAt(i));
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index a1adeaa..da8aeb5 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -16,78 +16,50 @@
 
 package com.android.server.am;
 
-import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
-import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
-import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.Process.NFC_UID;
-import static android.os.Process.ROOT_UID;
-import static android.os.Process.SHELL_UID;
-import static android.os.Process.SYSTEM_UID;
-
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ApplicationErrorReport;
 import android.app.ApplicationExitInfo;
 import android.app.ApplicationExitInfo.Reason;
 import android.app.ApplicationExitInfo.SubReason;
-import android.app.Dialog;
 import android.app.IApplicationThread;
-import android.content.ComponentName;
-import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.IncrementalStatesInfo;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.ProcessInfo;
-import android.content.pm.ServiceInfo;
 import android.content.pm.VersionedPackage;
 import android.content.res.CompatibilityInfo;
 import android.os.Binder;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.server.ServerProtoEnums;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.EventLog;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.CompositeRWLock;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.procstats.ProcessState;
 import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.MemoryPressureUtil;
 import com.android.server.wm.WindowProcessController;
 import com.android.server.wm.WindowProcessListener;
 
-import java.io.File;
 import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
-import java.util.function.Consumer;
 
 /**
  * Full information about a particular process that
@@ -97,6 +69,12 @@
     static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM;
 
     final ActivityManagerService mService; // where we came from
+    private final ActivityManagerGlobalLock mProcLock;
+
+    // =========================================================
+    // Basic info of the process, immutable or semi-immutable over
+    // the lifecycle of the process
+    // =========================================================
     volatile ApplicationInfo info; // all about the first app in the process
     final ProcessInfo processInfo; // if non-null, process-specific manifest info
     final boolean isolated;     // true if this is a special isolated process
@@ -106,243 +84,288 @@
     final String processName;   // name of the process
 
     /**
-     * List of packages running in the process
+     * Overall state of process's uid.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private UidRecord mUidRecord;
+
+    /**
+     * List of packages running in the process.
      */
     private final PackageList mPkgList = new PackageList(this);
 
-    UidRecord uidRecord;        // overall state of process's uid.
-    ArraySet<String> pkgDeps;   // additional packages we have a dependency on
-    IApplicationThread thread;  // the actual proc...  may be null only if
-                                // 'persistent' is true (in which case we
-                                // are in the process of launching the app)
-    int pid;                    // The process of this application; 0 if none
-    String procStatFile;        // path to /proc/<pid>/stat
-    int[] gids;                 // The gids this process was launched with
-    private String mRequiredAbi;// The ABI this process was launched with
-    String instructionSet;      // The instruction set this process was launched with
-    boolean starting;           // True if the process is being started
-    long lastActivityTime;      // For managing the LRU list
-    long lastStateTime;         // Last time setProcState changed
-    int maxAdj;                 // Maximum OOM adjustment for this process
-    private int mCurRawAdj;     // Current OOM unlimited adjustment for this process
-    int setRawAdj;              // Last set OOM unlimited adjustment for this process
-    int curAdj;                 // Current OOM adjustment for this process
-    int setAdj;                 // Last set OOM adjustment for this process
-    int verifiedAdj;            // The last adjustment that was verified as actually being set
-    int curCapability;          // Current capability flags of this process. For example,
-                                // PROCESS_CAPABILITY_FOREGROUND_LOCATION is one capability.
-    int setCapability;          // Last set capability flags.
-    long lastCompactTime;       // The last time that this process was compacted
-    int reqCompactAction;       // The most recent compaction action requested for this app.
-    int lastCompactAction;      // The most recent compaction action performed for this app.
-    boolean frozen;             // True when the process is frozen.
-    boolean freezerOverride;     // An override on the freeze state is in progress.
-    long freezeUnfreezeTime;    // Last time the app was (un)frozen, 0 for never
-    boolean shouldNotFreeze;    // True if a process has a WPRI binding from an unfrozen process
-    private int mCurSchedGroup; // Currently desired scheduling class
-    int setSchedGroup;          // Last set to background scheduling class
-    private int mCurProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
-    private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
-    private int mCurRawProcState = PROCESS_STATE_NONEXISTENT; // Temp state during computation
-    int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
-    int savedPriority;          // Previous priority value if we're switching to non-SCHED_OTHER
-    int renderThreadTid;        // TID for RenderThread
-    ServiceRecord connectionService; // Service that applied current connectionGroup/Importance
-    int connectionGroup;        // Last group set by a connection
-    int connectionImportance;   // Last importance set by a connection
-    boolean serviceb;           // Process currently is on the service B list
-    boolean serviceHighRam;     // We are forcing to service B list due to its RAM use
-    boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
-    private boolean mHasClientActivities;  // Are there any client services with activities?
-    boolean hasStartedServices; // Are there any started services running in this process?
-    private boolean mHasForegroundServices; // Running any services that are foreground?
-    private int mFgServiceTypes; // Type of foreground service, if there is a foreground service.
-    private int mRepFgServiceTypes; // Last reported foreground service types.
-    private boolean mHasForegroundActivities; // Running any activities that are foreground?
-    boolean repForegroundActivities; // Last reported foreground activities.
-    boolean systemNoUi;         // This is a system process, but not currently showing UI.
-    boolean hasShownUi;         // Has UI been shown in this process since it was started?
-    private boolean mHasTopUi;  // Is this process currently showing a non-activity UI that the user
-                                // is interacting with? E.g. The status bar when it is expanded, but
-                                // not when it is minimized. When true the
-                                // process will be set to use the ProcessList#SCHED_GROUP_TOP_APP
-                                // scheduling group to boost performance.
-    private boolean mHasOverlayUi; // Is the process currently showing a non-activity UI that
-                                // overlays on-top of activity UIs on screen. E.g. display a window
-                                // of type
-                                // android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY
-                                // When true the process will oom adj score will be set to
-                                // ProcessList#PERCEPTIBLE_APP_ADJ at minimum to reduce the chance
-                                // of the process getting killed.
-    boolean runningRemoteAnimation; // Is the process currently running a RemoteAnimation? When true
-                                // the process will be set to use the
-                                // ProcessList#SCHED_GROUP_TOP_APP scheduling group to boost
-                                // performance, as well as oom adj score will be set to
-                                // ProcessList#VISIBLE_APP_ADJ at minimum to reduce the chance
-                                // of the process getting killed.
-    boolean hasAboveClient;     // Bound using BIND_ABOVE_CLIENT, so want to be lower
-    boolean treatLikeActivity;  // Bound using BIND_TREAT_LIKE_ACTIVITY
-    boolean bad;                // True if disabled in the bad process list
-    boolean killedByAm;         // True when proc has been killed by activity manager, not for RAM
-    boolean killed;             // True once we know the process has been killed
-    boolean procStateChanged;   // Keep track of whether we changed 'setAdj'.
-    boolean reportedInteraction;// Whether we have told usage stats about it being an interaction
-    boolean unlocked;           // True when proc was started in user unlocked state
-    private long mInteractionEventTime; // The time we sent the last interaction event
-    private long mFgInteractionTime; // When we became foreground for interaction purposes
-    String waitingToKill;       // Process is waiting to be killed when in the bg, and reason
-    Object forcingToImportant;  // Token that is forcing this process to be important
-    int adjSeq;                 // Sequence id for identifying oom_adj assignment cycles
-    int completedAdjSeq;        // Sequence id for identifying oom_adj assignment cycles
-    boolean containsCycle;      // Whether this app has encountered a cycle in the most recent update
-    int lruSeq;                 // Sequence id for identifying LRU update cycles
-    CompatibilityInfo compat;   // last used compatibility mode
-    IBinder.DeathRecipient deathRecipient; // Who is watching for the death.
-    private ActiveInstrumentation mInstr; // Set to currently active instrumentation running in
-                                          // process.
-    private boolean mUsingWrapper; // Set to true when process was launched with a wrapper attached
-    final ArraySet<BroadcastRecord> curReceivers = new ArraySet<BroadcastRecord>();// receivers currently running in the app
-    private long mWhenUnimportant; // When (uptime) the process last became unimportant
-    long lastProviderTime;      // The last time someone else was using a provider in this process.
-    long lastTopTime;           // The last time the process was in the TOP state or greater.
-    boolean empty;              // Is this an empty background process?
-    private boolean mCached;    // Is this a cached process?
-    String adjType;             // Debugging: primary thing impacting oom_adj.
-    int adjTypeCode;            // Debugging: adj code to report to app.
-    Object adjSource;           // Debugging: option dependent object.
-    int adjSourceProcState;     // Debugging: proc state of adjSource's process.
-    Object adjTarget;           // Debugging: target component impacting oom_adj.
-    Runnable crashHandler;      // Optional local handler to be invoked in the process crash.
-    boolean bindMountPending;   // True if Android/obb and Android/data need to be bind mount .
+    /**
+     * Additional packages we have a dependency on.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private ArraySet<String> mPkgDeps;
 
-    // Controller for error dialogs
-    private final ErrorDialogController mDialogController = new ErrorDialogController();
-    // Controller for driving the process state on the window manager side.
+    /**
+     * The process of this application; 0 if none.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    int mPid;
+
+    /**
+     * The gids this process was launched with.
+     */
+    @GuardedBy("mService")
+    private int[] mGids;
+
+    /**
+     * The ABI this process was launched with.
+     */
+    @GuardedBy("mService")
+    private String mRequiredAbi;
+
+    /**
+     * The instruction set this process was launched with.
+     */
+    @GuardedBy("mService")
+    private String mInstructionSet;
+
+    /**
+     * The actual proc...  may be null only if 'persistent' is true
+     * (in which case we are in the process of launching the app).
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private IApplicationThread mThread;
+
+    /**
+     * Always keep this application running?
+     */
+    private volatile boolean mPersistent;
+
+    /**
+     * Caching of toShortString() result.
+     * <p>Note: No lock here, it doesn't matter in case of race condition</p>
+     */
+    private String mShortStringName;
+
+    /**
+     * Caching of toString() result.
+     * <p>Note: No lock here, it doesn't matter in case of race condition</p>
+     */
+    private String mStringName;
+
+    /**
+     * Process start is pending.
+     */
+    @GuardedBy("mService")
+    private boolean mPendingStart;
+
+    /**
+     * Seq no. Indicating the latest process start associated with this process record.
+     */
+    @GuardedBy("mService")
+    private long mStartSeq;
+
+    /**
+     * Params used in starting this process.
+     */
+    private volatile HostingRecord mHostingRecord;
+
+    /**
+     * Selinux info of this process.
+     */
+    private volatile String mSeInfo;
+
+    /**
+     * When the process is started.
+     */
+    private volatile long mStartTime;
+
+    /**
+     * This will be same as {@link #uid} usually except for some apps used during factory testing.
+     */
+    private volatile int mStartUid;
+
+    /**
+     * Indicates how the external storage was mounted for this process.
+     */
+    private volatile int mMountMode;
+
+    /**
+     * True if Android/obb and Android/data need to be bind mount.
+     */
+    private volatile boolean mBindMountPending;
+
+    /**
+     * True when proc was started in user unlocked state.
+     */
+    @GuardedBy("mProcLock")
+    private boolean mUnlocked;
+
+    /**
+     * TID for RenderThread.
+     */
+    @GuardedBy("mProcLock")
+    private int mRenderThreadTid;
+
+    /**
+     * Last used compatibility mode.
+     */
+    @GuardedBy("mService")
+    private CompatibilityInfo mCompat;
+
+    /**
+     * Set of disabled compat changes for the process (all others are enabled).
+     */
+    @GuardedBy("mService")
+    private long[] mDisabledCompatChanges;
+
+    /**
+     * Who is watching for the death.
+     */
+    @GuardedBy("mService")
+    private IBinder.DeathRecipient mDeathRecipient;
+
+    /**
+     * Set to currently active instrumentation running in process.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private ActiveInstrumentation mInstr;
+
+    /**
+     * True when proc has been killed by activity manager, not for RAM.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mKilledByAm;
+
+    /**
+     * True once we know the process has been killed.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mKilled;
+
+    /**
+     * The timestamp in uptime when this process was killed.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private long mKillTime;
+
+    /**
+     * Process is waiting to be killed when in the bg, and reason.
+     */
+    @GuardedBy("mService")
+    private String mWaitingToKill;
+
+    /**
+     * Whether this process should be killed and removed from process list.
+     * It is set when the package is force-stopped or the process has crashed too many times.
+     */
+    private volatile boolean mRemoved;
+
+    /**
+     * Was app launched for debugging?
+     */
+    @GuardedBy("mService")
+    private boolean mDebugging;
+
+    /**
+     * Has process show wait for debugger dialog?
+     */
+    @GuardedBy("mProcLock")
+    private boolean mWaitedForDebugger;
+
+    /**
+     * For managing the LRU list.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private long mLastActivityTime;
+
+    /**
+     * Set to true when process was launched with a wrapper attached.
+     */
+    @GuardedBy("mService")
+    private boolean mUsingWrapper;
+
+    /**
+     * Sequence id for identifying LRU update cycles.
+     */
+    @GuardedBy("mService")
+    private int mLruSeq;
+
+    /**
+     * Class to run on start if this is a special isolated process.
+     */
+    @GuardedBy("mService")
+    private String mIsolatedEntryPoint;
+
+    /**
+     * Arguments to pass to isolatedEntryPoint's main().
+     */
+    @GuardedBy("mService")
+    private String[] mIsolatedEntryPointArgs;
+
+    /**
+     * Process is currently hosting a backup agent for backup or restore.
+     */
+    @GuardedBy("mService")
+    private boolean mInFullBackup;
+
+    /**
+     * Controller for driving the process state on the window manager side.
+     */
     private final WindowProcessController mWindowProcessController;
-    // all ServiceRecord running in this process
-    private final ArraySet<ServiceRecord> mServices = new ArraySet<>();
-    // services that are currently executing code (need to remain foreground).
-    final ArraySet<ServiceRecord> executingServices = new ArraySet<>();
-    // All ConnectionRecord this process holds
-    final ArraySet<ConnectionRecord> connections = new ArraySet<>();
-    // all IIntentReceivers that are registered from this process.
-    final ArraySet<ReceiverList> receivers = new ArraySet<>();
-    // class (String) -> ContentProviderRecord
-    final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
-    // All ContentProviderRecord process is using
-    final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
-    // a set of UIDs of all bound clients
-    private ArraySet<Integer> mBoundClientUids = new ArraySet<>();
-
-    String isolatedEntryPoint;  // Class to run on start if this is a special isolated process.
-    String[] isolatedEntryPointArgs; // Arguments to pass to isolatedEntryPoint's main().
-
-    boolean execServicesFg;     // do we need to be executing services in the foreground?
-    private boolean mPersistent;// always keep this application running?
-    private boolean mCrashing;  // are we in the process of crashing?
-    boolean forceCrashReport;   // suppress normal auto-dismiss of crash dialog & report UI?
-    private boolean mNotResponding; // does the app have a not responding dialog?
-    volatile boolean removed;   // Whether this process should be killed and removed from process
-                                // list. It is set when the package is force-stopped or the process
-                                // has crashed too many times.
-    private boolean mDebugging; // was app launched for debugging?
-    boolean waitedForDebugger;  // has process show wait for debugger dialog?
-
-    String shortStringName;     // caching of toShortString() result.
-    String stringName;          // caching of toString() result.
-    boolean pendingStart;       // Process start is pending.
-    long startSeq;              // Seq no. indicating the latest process start associated with
-                                // this process record.
-    int mountMode;              // Indicates how the external storage was mounted for this process.
-
-    // These reports are generated & stored when an app gets into an error condition.
-    // They will be "null" when all is OK.
-    ActivityManager.ProcessErrorStateInfo crashingReport;
-    ActivityManager.ProcessErrorStateInfo notRespondingReport;
-
-    // Who will be notified of the error. This is usually an activity in the
-    // app that installed the package.
-    ComponentName errorReportReceiver;
-
-    // Process is currently hosting a backup agent for backup or restore
-    public boolean inFullBackup;
-    // App is allowed to manage allowlists such as temporary Power Save mode allowlist.
-    boolean mAllowlistManager;
-
-    // Params used in starting this process.
-    HostingRecord hostingRecord;
-    String seInfo;
-    long startTime;
-    // This will be same as {@link #uid} usually except for some apps used during factory testing.
-    int startUid;
-    // set of disabled compat changes for the process (all others are enabled)
-    long[] mDisabledCompatChanges;
-
-    // The precede instance of the process, which would exist when the previous process is killed
-    // but not fully dead yet; in this case, the new instance of the process should be held until
-    // this precede instance is fully dead.
-    volatile ProcessRecord mPrecedence;
-    // The succeeding instance of the process, which is going to be started after this process
-    // is killed successfully.
-    volatile ProcessRecord mSuccessor;
-
-    // Cached task info for OomAdjuster
-    private static final int VALUE_INVALID = -1;
-    private static final int VALUE_FALSE = 0;
-    private static final int VALUE_TRUE = 1;
-    private int mCachedHasActivities = VALUE_INVALID;
-    private int mCachedIsHeavyWeight = VALUE_INVALID;
-    private int mCachedHasVisibleActivities = VALUE_INVALID;
-    private int mCachedIsHomeProcess = VALUE_INVALID;
-    private int mCachedIsPreviousProcess = VALUE_INVALID;
-    private int mCachedHasRecentTasks = VALUE_INVALID;
-    private int mCachedIsReceivingBroadcast = VALUE_INVALID;
-    int mCachedAdj = ProcessList.INVALID_ADJ;
-    boolean mCachedForegroundActivities = false;
-    int mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-    int mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-
-    // Approximates the usage count of the app, used for cache re-ranking by CacheOomRanker.
-    //
-    // Counts the number of times the process is re-added to the cache (i.e. setCached(false);
-    // setCached(true)). This over counts, as setCached is sometimes reset while remaining in the
-    // cache. However, this happens uniformly across processes, so ranking is not affected.
-    private int mCacheOomRankerUseCount;
-
-    boolean mReachable; // Whether or not this process is reachable from given process
-
-    long mKillTime; // The timestamp in uptime when this process was killed.
-
-    // If the proc state is PROCESS_STATE_BOUND_FOREGROUND_SERVICE or above, it can start FGS.
-    // It must obtain the proc state from a persistent/top process or FGS, not transitive.
-    int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
-
-    private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>();
-
-    // The list of permissions that can start FGS from background.
-    private static String[] ALLOW_BG_START_FGS_PERMISSIONS =
-            {START_ACTIVITIES_FROM_BACKGROUND, START_FOREGROUND_SERVICES_FROM_BACKGROUND,
-                    SYSTEM_ALERT_WINDOW};
-    // Does the process has permission to start FGS from background.
-    boolean mAllowStartFgsByPermission;
-    // Can this process start FGS from background?
-    // If this process has the ability to start FGS from background, this ability can be passed to
-    // another process through service binding.
-    boolean mAllowStartFgs;
 
     /**
      * Profiling info of the process, such as PSS, cpu, etc.
      */
     final ProcessProfileRecord mProfile;
 
+    /**
+     * All about the services in this process.
+     */
+    final ProcessServiceRecord mServices;
+
+    /**
+     * All about the providers in this process.
+     */
+    final ProcessProviderRecord mProviders;
+
+    /**
+     * All about the receivers in this process.
+     */
+    final ProcessReceiverRecord mReceivers;
+
+    /**
+     * All about the error state(crash, ANR) in this process.
+     */
+    final ProcessErrorStateRecord mErrorState;
+
+    /**
+     * All about the process state info (proc state, oom adj score) in this process.
+     */
+    final ProcessStateRecord mState;
+
+    /**
+     * All about the state info of the optimizer when the process is cached.
+     */
+    final ProcessCachedOptimizerRecord mOptRecord;
+
+    /**
+     * The preceding instance of the process, which would exist when the previous process is killed
+     * but not fully dead yet; in this case, the new instance of the process should be held until
+     * this preceding instance is fully dead.
+     */
+    volatile ProcessRecord mPredecessor;
+
+    /**
+     * The succeeding instance of the process, which is going to be started after this process
+     * is killed successfully.
+     */
+    volatile ProcessRecord mSuccessor;
+
     void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
             long startTime) {
-        this.startUid = startUid;
-        this.hostingRecord = hostingRecord;
-        this.seInfo = seInfo;
-        this.startTime = startTime;
+        this.mStartUid = startUid;
+        this.mHostingRecord = hostingRecord;
+        this.mSeInfo = seInfo;
+        this.mStartTime = startTime;
     }
 
+    @GuardedBy({"mService", "mProcLock"})
     void dump(PrintWriter pw, String prefix) {
         final long nowUptime = SystemClock.uptimeMillis();
 
@@ -352,10 +375,10 @@
             pw.print(" ISOLATED uid="); pw.print(uid);
         }
         pw.print(" gids={");
-        if (gids != null) {
-            for (int gi=0; gi<gids.length; gi++) {
+        if (mGids != null) {
+            for (int gi = 0; gi < mGids.length; gi++) {
                 if (gi != 0) pw.print(", ");
-                pw.print(gids[gi]);
+                pw.print(mGids[gi]);
 
             }
         }
@@ -371,9 +394,12 @@
             if (processInfo.gwpAsanMode != ApplicationInfo.GWP_ASAN_DEFAULT) {
                 pw.print(prefix); pw.println("  gwpAsanMode=" + processInfo.gwpAsanMode);
             }
+            if (processInfo.memtagMode != ApplicationInfo.MEMTAG_DEFAULT) {
+                pw.print(prefix); pw.println("  memtagMode=" + processInfo.memtagMode);
+            }
         }
         pw.print(prefix); pw.print("mRequiredAbi="); pw.print(mRequiredAbi);
-                pw.print(" instructionSet="); pw.println(instructionSet);
+        pw.print(" instructionSet="); pw.println(mInstructionSet);
         if (info.className != null) {
             pw.print(prefix); pw.print("class="); pw.println(info.className);
         }
@@ -386,210 +412,62 @@
                 pw.print(" publicDir="); pw.print(info.publicSourceDir);
                 pw.print(" data="); pw.println(info.dataDir);
         mPkgList.dump(pw, prefix);
-        pw.println("}");
-        if (pkgDeps != null) {
+        if (mPkgDeps != null) {
             pw.print(prefix); pw.print("packageDependencies={");
-            for (int i=0; i<pkgDeps.size(); i++) {
+            for (int i = 0; i < mPkgDeps.size(); i++) {
                 if (i > 0) pw.print(", ");
-                pw.print(pkgDeps.valueAt(i));
+                pw.print(mPkgDeps.valueAt(i));
             }
             pw.println("}");
         }
-        pw.print(prefix); pw.print("compat="); pw.println(compat);
+        pw.print(prefix); pw.print("compat="); pw.println(mCompat);
         if (mInstr != null) {
             pw.print(prefix); pw.print("mInstr="); pw.println(mInstr);
         }
-        pw.print(prefix); pw.print("thread="); pw.println(thread);
-        pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
-                pw.println(starting);
+        pw.print(prefix); pw.print("thread="); pw.println(mThread);
+        pw.print(prefix); pw.print("pid="); pw.print(mPid);
         pw.print(prefix); pw.print("lastActivityTime=");
-                TimeUtils.formatDuration(lastActivityTime, nowUptime, pw);
-                pw.println();
-        pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
-                pw.print(" lruSeq="); pw.println(lruSeq);
-        pw.print(prefix); pw.print("oom adj: max="); pw.print(maxAdj);
-                pw.print(" curRaw="); pw.print(mCurRawAdj);
-                pw.print(" setRaw="); pw.print(setRawAdj);
-                pw.print(" cur="); pw.print(curAdj);
-                pw.print(" set="); pw.println(setAdj);
-        pw.print(prefix); pw.print("lastCompactTime="); pw.print(lastCompactTime);
-                pw.print(" lastCompactAction="); pw.println(lastCompactAction);
-        pw.print(prefix); pw.print("mCurSchedGroup="); pw.print(mCurSchedGroup);
-                pw.print(" setSchedGroup="); pw.print(setSchedGroup);
-                pw.print(" systemNoUi="); pw.print(systemNoUi);
-        pw.print(prefix); pw.print("curProcState="); pw.print(getCurProcState());
-                pw.print(" mRepProcState="); pw.print(mRepProcState);
-                pw.print(" setProcState="); pw.print(setProcState);
-                pw.print(" lastStateTime=");
-                TimeUtils.formatDuration(lastStateTime, nowUptime, pw);
-                pw.println();
-        pw.print(prefix); pw.print("curCapability=");
-                ActivityManager.printCapabilitiesFull(pw, curCapability);
-                pw.print(" setCapability=");
-                ActivityManager.printCapabilitiesFull(pw, setCapability);
-                pw.println();
-        pw.print(prefix); pw.print("allowStartFgsState=");
-                pw.println(mAllowStartFgsState);
-        if (mAllowStartFgs) {
-            pw.print(prefix); pw.print("allowStartFgs="); pw.println(mAllowStartFgs);
-        }
-        if (hasShownUi || mProfile.hasPendingUiClean() || hasAboveClient || treatLikeActivity) {
-            pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
-            pw.print(" pendingUiClean="); pw.print(mProfile.hasPendingUiClean());
-            pw.print(" hasAboveClient="); pw.print(hasAboveClient);
-            pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
-        }
-        pw.print(prefix); pw.print("cached="); pw.print(mCached);
-                pw.print(" empty="); pw.println(empty);
-        if (serviceb) {
-            pw.print(prefix); pw.print("serviceb="); pw.print(serviceb);
-                    pw.print(" serviceHighRam="); pw.println(serviceHighRam);
-        }
-        if (notCachedSinceIdle) {
-            pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(notCachedSinceIdle);
-            pw.print(" initialIdlePss="); pw.println(mProfile.getInitialIdlePss());
-        }
-        if (connectionService != null || connectionGroup != 0) {
-            pw.print(prefix); pw.print("connectionGroup="); pw.print(connectionGroup);
-            pw.print(" Importance="); pw.print(connectionImportance);
-            pw.print(" Service="); pw.println(connectionService);
-        }
-        if (hasTopUi() || hasOverlayUi() || runningRemoteAnimation) {
-            pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
-                    pw.print(" hasOverlayUi="); pw.print(hasOverlayUi());
-                    pw.print(" runningRemoteAnimation="); pw.println(runningRemoteAnimation);
-        }
-        if (mHasForegroundServices || forcingToImportant != null) {
-            pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices);
-                    pw.print(" forcingToImportant="); pw.println(forcingToImportant);
-        }
-        if (reportedInteraction || mFgInteractionTime != 0) {
-            pw.print(prefix); pw.print("reportedInteraction=");
-            pw.print(reportedInteraction);
-            if (mInteractionEventTime != 0) {
-                pw.print(" time=");
-                TimeUtils.formatDuration(mInteractionEventTime, SystemClock.elapsedRealtime(), pw);
-            }
-            if (mFgInteractionTime != 0) {
-                pw.print(" fgInteractionTime=");
-                TimeUtils.formatDuration(mFgInteractionTime, SystemClock.elapsedRealtime(), pw);
-            }
-            pw.println();
-        }
-        if (mPersistent || removed) {
+        TimeUtils.formatDuration(mLastActivityTime, nowUptime, pw);
+        if (mPersistent || mRemoved) {
             pw.print(prefix); pw.print("persistent="); pw.print(mPersistent);
-                    pw.print(" removed="); pw.println(removed);
+            pw.print(" removed="); pw.println(mRemoved);
         }
-        if (mHasClientActivities || mHasForegroundActivities || repForegroundActivities) {
-            pw.print(prefix); pw.print("hasClientActivities="); pw.print(mHasClientActivities);
-                    pw.print(" foregroundActivities="); pw.print(mHasForegroundActivities);
-                    pw.print(" (rep="); pw.print(repForegroundActivities); pw.println(")");
+        if (mDebugging) {
+            pw.print(prefix); pw.print("mDebugging="); pw.println(mDebugging);
         }
-        if (lastProviderTime > 0) {
-            pw.print(prefix); pw.print("lastProviderTime=");
-            TimeUtils.formatDuration(lastProviderTime, nowUptime, pw);
-            pw.println();
+        if (mPendingStart) {
+            pw.print(prefix); pw.print("pendingStart="); pw.println(mPendingStart);
         }
-        if (lastTopTime > 0) {
-            pw.print(prefix); pw.print("lastTopTime=");
-            TimeUtils.formatDuration(lastTopTime, nowUptime, pw);
-            pw.println();
-        }
-        if (hasStartedServices) {
-            pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices);
-        }
-        if (pendingStart) {
-            pw.print(prefix); pw.print("pendingStart="); pw.println(pendingStart);
-        }
-        pw.print(prefix); pw.print("startSeq="); pw.println(startSeq);
+        pw.print(prefix); pw.print("startSeq="); pw.println(mStartSeq);
         pw.print(prefix); pw.print("mountMode="); pw.println(
-                DebugUtils.valueToString(Zygote.class, "MOUNT_EXTERNAL_", mountMode));
-        if (setProcState > ActivityManager.PROCESS_STATE_SERVICE) {
+                DebugUtils.valueToString(Zygote.class, "MOUNT_EXTERNAL_", mMountMode));
+        if (mKilled || mKilledByAm || mWaitingToKill != null) {
+            pw.print(prefix); pw.print("killed="); pw.print(mKilled);
+            pw.print(" killedByAm="); pw.print(mKilledByAm);
+            pw.print(" waitingToKill="); pw.println(mWaitingToKill);
+        }
+        if (mIsolatedEntryPoint != null || mIsolatedEntryPointArgs != null) {
+            pw.print(prefix); pw.print("isolatedEntryPoint="); pw.println(mIsolatedEntryPoint);
+            pw.print(prefix); pw.print("isolatedEntryPointArgs=");
+            pw.println(Arrays.toString(mIsolatedEntryPointArgs));
+        }
+        if (mState.getSetProcState() > ActivityManager.PROCESS_STATE_SERVICE) {
             mProfile.dumpCputime(pw, prefix);
-            pw.print(" whenUnimportant=");
-            TimeUtils.formatDuration(mWhenUnimportant - nowUptime, pw);
-            pw.println();
         }
         mProfile.dumpPss(pw, prefix, nowUptime);
-        if (killed || killedByAm || waitingToKill != null) {
-            pw.print(prefix); pw.print("killed="); pw.print(killed);
-                    pw.print(" killedByAm="); pw.print(killedByAm);
-                    pw.print(" waitingToKill="); pw.println(waitingToKill);
-        }
-        if (mDebugging || mCrashing || mDialogController.hasCrashDialogs() || mNotResponding
-                || mDialogController.hasAnrDialogs() || bad) {
-            pw.print(prefix); pw.print("mDebugging="); pw.print(mDebugging);
-            pw.print(" mCrashing=" + mCrashing);
-            pw.print(" " + mDialogController.mCrashDialogs);
-            pw.print(" mNotResponding=" + mNotResponding);
-            pw.print(" " + mDialogController.mAnrDialogs);
-            pw.print(" bad=" + bad);
-
-            // mCrashing or mNotResponding is always set before errorReportReceiver
-            if (errorReportReceiver != null) {
-                pw.print(" errorReportReceiver=");
-                pw.print(errorReportReceiver.flattenToShortString());
-            }
-            pw.println();
-        }
-        if (mAllowlistManager) {
-            pw.print(prefix); pw.print("allowlistManager="); pw.println(mAllowlistManager);
-        }
-        if (isolatedEntryPoint != null || isolatedEntryPointArgs != null) {
-            pw.print(prefix); pw.print("isolatedEntryPoint="); pw.println(isolatedEntryPoint);
-            pw.print(prefix); pw.print("isolatedEntryPointArgs=");
-            pw.println(Arrays.toString(isolatedEntryPointArgs));
-        }
+        mState.dump(pw, prefix, nowUptime);
+        mErrorState.dump(pw, prefix, nowUptime);
+        mServices.dump(pw, prefix, nowUptime);
+        mProviders.dump(pw, prefix, nowUptime);
+        mReceivers.dump(pw, prefix, nowUptime);
+        mOptRecord.dump(pw, prefix, nowUptime);
         mWindowProcessController.dump(pw, prefix);
-        if (mServices.size() > 0) {
-            pw.print(prefix); pw.println("Services:");
-            for (int i = 0; i < mServices.size(); i++) {
-                pw.print(prefix); pw.print("  - "); pw.println(mServices.valueAt(i));
-            }
-        }
-        if (executingServices.size() > 0) {
-            pw.print(prefix); pw.print("Executing Services (fg=");
-            pw.print(execServicesFg); pw.println(")");
-            for (int i=0; i<executingServices.size(); i++) {
-                pw.print(prefix); pw.print("  - "); pw.println(executingServices.valueAt(i));
-            }
-        }
-        if (connections.size() > 0) {
-            pw.print(prefix); pw.println("Connections:");
-            for (int i=0; i<connections.size(); i++) {
-                pw.print(prefix); pw.print("  - "); pw.println(connections.valueAt(i));
-            }
-        }
-        if (pubProviders.size() > 0) {
-            pw.print(prefix); pw.println("Published Providers:");
-            for (int i=0; i<pubProviders.size(); i++) {
-                pw.print(prefix); pw.print("  - "); pw.println(pubProviders.keyAt(i));
-                pw.print(prefix); pw.print("    -> "); pw.println(pubProviders.valueAt(i));
-            }
-        }
-        if (conProviders.size() > 0) {
-            pw.print(prefix); pw.println("Connected Providers:");
-            for (int i=0; i<conProviders.size(); i++) {
-                pw.print(prefix); pw.print("  - "); pw.println(conProviders.get(i).toShortString());
-            }
-        }
-        if (!curReceivers.isEmpty()) {
-            pw.print(prefix); pw.println("Current Receivers:");
-            for (int i=0; i < curReceivers.size(); i++) {
-                pw.print(prefix); pw.print("  - "); pw.println(curReceivers.valueAt(i));
-            }
-        }
-        if (receivers.size() > 0) {
-            pw.print(prefix); pw.println("Receivers:");
-            for (int i=0; i<receivers.size(); i++) {
-                pw.print(prefix); pw.print("  - "); pw.println(receivers.valueAt(i));
-            }
-        }
     }
 
     ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName,
             int _uid) {
         mService = _service;
+        mProcLock = _service.mProcLock;
         info = _info;
         ProcessInfo procInfo = null;
         if (_service.mPackageManagerInt != null) {
@@ -598,9 +476,12 @@
             if (processes != null) {
                 procInfo = processes.get(_processName);
                 if (procInfo != null && procInfo.deniedPermissions == null
-                        && procInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_DEFAULT) {
+                        && procInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_DEFAULT
+                        && procInfo.memtagMode == ApplicationInfo.MEMTAG_DEFAULT
+                        && procInfo.nativeHeapZeroInit == null) {
                     // If this process hasn't asked for permissions to be denied, or for a
-                    // non-default GwpAsan mode, then we don't care about it.
+                    // non-default GwpAsan mode, or any other non-default setting, then we don't
+                    // care about it.
                     procInfo = null;
                 }
             }
@@ -612,118 +493,393 @@
         uid = _uid;
         userId = UserHandle.getUserId(_uid);
         processName = _processName;
-        maxAdj = ProcessList.UNKNOWN_ADJ;
-        mCurRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
-        curAdj = setAdj = verifiedAdj = ProcessList.INVALID_ADJ;
         mPersistent = false;
-        removed = false;
+        mRemoved = false;
         mProfile = new ProcessProfileRecord(this);
+        mServices = new ProcessServiceRecord(this);
+        mProviders = new ProcessProviderRecord(this);
+        mReceivers = new ProcessReceiverRecord(this);
+        mErrorState = new ProcessErrorStateRecord(this);
+        mState = new ProcessStateRecord(this);
+        mOptRecord = new ProcessCachedOptimizerRecord(this);
         final long now = SystemClock.uptimeMillis();
-        freezeUnfreezeTime = lastStateTime = now;
         mProfile.init(now);
+        mOptRecord.init(now);
+        mState.init(now);
         mWindowProcessController = new WindowProcessController(
                 mService.mActivityTaskManager, info, processName, uid, userId, this, this);
         mPkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
-        setAllowStartFgsByPermission();
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    UidRecord getUidRecord() {
+        return mUidRecord;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setUidRecord(UidRecord uidRecord) {
+        mUidRecord = uidRecord;
     }
 
     PackageList getPkgList() {
         return mPkgList;
     }
 
-    public void setPid(int _pid) {
-        pid = _pid;
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    ArraySet<String> getPkgDeps() {
+        return mPkgDeps;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setPkgDeps(ArraySet<String> pkgDeps) {
+        mPkgDeps = pkgDeps;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getPid() {
+        return mPid;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setPid(int pid) {
+        mPid = pid;
         mWindowProcessController.setPid(pid);
-        procStatFile = null;
-        shortStringName = null;
-        stringName = null;
+        mShortStringName = null;
+        mStringName = null;
         synchronized (mProfile.mProfilerLock) {
             mProfile.setPid(pid);
         }
     }
 
-    public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {
-        mProfile.onProcessActive(_thread, tracker);
-        thread = _thread;
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    IApplicationThread getThread() {
+        return mThread;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    public void makeActive(IApplicationThread thread, ProcessStatsService tracker) {
+        mProfile.onProcessActive(thread, tracker);
+        mThread = thread;
         mWindowProcessController.setThread(thread);
     }
 
+    @GuardedBy({"mService", "mProcLock"})
     public void makeInactive(ProcessStatsService tracker) {
-        thread = null;
+        mThread = null;
         mWindowProcessController.setThread(null);
         mProfile.onProcessInactive(tracker);
     }
 
-    /**
-     * Records a service as running in the process. Note that this method does not actually start
-     * the service, but records the service as started for bookkeeping.
-     *
-     * @return true if the service was added, false otherwise.
-     */
-    boolean startService(ServiceRecord record) {
-        if (record == null) {
-            return false;
+    @GuardedBy("mService")
+    int[] getGids() {
+        return mGids;
+    }
+
+    @GuardedBy("mService")
+    void setGids(int[] gids) {
+        mGids = gids;
+    }
+
+    @GuardedBy("mService")
+    String getRequiredAbi() {
+        return mRequiredAbi;
+    }
+
+    @GuardedBy("mService")
+    void setRequiredAbi(String requiredAbi) {
+        mRequiredAbi = requiredAbi;
+        mWindowProcessController.setRequiredAbi(requiredAbi);
+    }
+
+    @GuardedBy("mService")
+    String getInstructionSet() {
+        return mInstructionSet;
+    }
+
+    @GuardedBy("mService")
+    void setInstructionSet(String instructionSet) {
+        mInstructionSet = instructionSet;
+    }
+
+    void setPersistent(boolean persistent) {
+        mPersistent = persistent;
+        mWindowProcessController.setPersistent(persistent);
+    }
+
+    boolean isPersistent() {
+        return mPersistent;
+    }
+
+    @GuardedBy("mService")
+    boolean isPendingStart() {
+        return mPendingStart;
+    }
+
+    @GuardedBy("mService")
+    void setPendingStart(boolean pendingStart) {
+        mPendingStart = pendingStart;
+    }
+
+    @GuardedBy("mService")
+    long getStartSeq() {
+        return mStartSeq;
+    }
+
+    @GuardedBy("mService")
+    void setStartSeq(long startSeq) {
+        mStartSeq = startSeq;
+    }
+
+    HostingRecord getHostingRecord() {
+        return mHostingRecord;
+    }
+
+    void setHostingRecord(HostingRecord hostingRecord) {
+        mHostingRecord = hostingRecord;
+    }
+
+    String getSeInfo() {
+        return mSeInfo;
+    }
+
+    void setSeInfo(String seInfo) {
+        mSeInfo = seInfo;
+    }
+
+    long getStartTime() {
+        return mStartTime;
+    }
+
+    void setStartTime(long startTime) {
+        mStartTime = startTime;
+    }
+
+    int getStartUid() {
+        return mStartUid;
+    }
+
+    void setStartUid(int startUid) {
+        mStartUid = startUid;
+    }
+
+    int getMountMode() {
+        return mMountMode;
+    }
+
+    void setMountMode(int mountMode) {
+        mMountMode = mountMode;
+    }
+
+    boolean isBindMountPending() {
+        return mBindMountPending;
+    }
+
+    void setBindMountPending(boolean bindMountPending) {
+        mBindMountPending = bindMountPending;
+    }
+
+    @GuardedBy("mProcLock")
+    boolean isUnlocked() {
+        return mUnlocked;
+    }
+
+    @GuardedBy("mProcLock")
+    void setUnlocked(boolean unlocked) {
+        mUnlocked = unlocked;
+    }
+
+    @GuardedBy("mProcLock")
+    int getRenderThreadTid() {
+        return mRenderThreadTid;
+    }
+
+    @GuardedBy("mProcLock")
+    void setRenderThreadTid(int renderThreadTid) {
+        mRenderThreadTid = renderThreadTid;
+    }
+
+    @GuardedBy("mService")
+    CompatibilityInfo getCompat() {
+        return mCompat;
+    }
+
+    @GuardedBy("mService")
+    void setCompat(CompatibilityInfo compat) {
+        mCompat = compat;
+    }
+
+    @GuardedBy("mService")
+    long[] getDisabledCompatChanges() {
+        return mDisabledCompatChanges;
+    }
+
+    @GuardedBy("mService")
+    void setDisabledCompatChanges(long[] disabledCompatChanges) {
+        mDisabledCompatChanges = disabledCompatChanges;
+    }
+
+    @GuardedBy("mService")
+    void unlinkDeathRecipient() {
+        if (mDeathRecipient != null && mThread != null) {
+            mThread.asBinder().unlinkToDeath(mDeathRecipient, 0);
         }
-        boolean added = mServices.add(record);
-        if (added && record.serviceInfo != null) {
-            mWindowProcessController.onServiceStarted(record.serviceInfo);
-        }
-        return added;
+        mDeathRecipient = null;
     }
 
-    /**
-     * Records a service as stopped. Note that like {@link #startService(ServiceRecord)} this method
-     * does not actually stop the service, but records the service as stopped for bookkeeping.
-     *
-     * @return true if the service was removed, false otherwise.
-     */
-    boolean stopService(ServiceRecord record) {
-        return mServices.remove(record);
+    @GuardedBy("mService")
+    void setDeathRecipient(IBinder.DeathRecipient deathRecipient) {
+        mDeathRecipient = deathRecipient;
     }
 
-    /**
-     * The same as calling {@link #stopService(ServiceRecord)} on all current running services.
-     */
-    void stopAllServices() {
-        mServices.clear();
+    @GuardedBy({"mService", "mProcLock"})
+    void setActiveInstrumentation(ActiveInstrumentation instr) {
+        mInstr = instr;
+        boolean isInstrumenting = instr != null;
+        mWindowProcessController.setInstrumenting(
+                isInstrumenting,
+                isInstrumenting ? instr.mSourceUid : -1,
+                isInstrumenting && instr.mHasBackgroundActivityStartsPermission);
     }
 
-    /**
-     * Returns the number of services added with {@link #startService(ServiceRecord)} and not yet
-     * removed by a call to {@link #stopService(ServiceRecord)} or {@link #stopAllServices()}.
-     *
-     * @see #startService(ServiceRecord)
-     * @see #stopService(ServiceRecord)
-     */
-    int numberOfRunningServices() {
-        return mServices.size();
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    ActiveInstrumentation getActiveInstrumentation() {
+        return mInstr;
     }
 
-    /**
-     * Returns the service at the specified {@code index}.
-     *
-     * @see #numberOfRunningServices()
-     */
-    ServiceRecord getRunningServiceAt(int index) {
-        return mServices.valueAt(index);
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isKilledByAm() {
+        return mKilledByAm;
     }
 
-    void setCached(boolean cached) {
-        if (mCached != cached) {
-            mCached = cached;
-            if (cached) {
-                ++mCacheOomRankerUseCount;
-            }
-        }
+    @GuardedBy({"mService", "mProcLock"})
+    void setKilledByAm(boolean killedByAm) {
+        mKilledByAm = killedByAm;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isKilled() {
+        return mKilled;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setKilled(boolean killed) {
+        mKilled = killed;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    long getKillTime() {
+        return mKillTime;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setKillTime(long killTime) {
+        mKillTime = killTime;
+    }
+
+    @GuardedBy("mService")
+    String getWaitingToKill() {
+        return mWaitingToKill;
+    }
+
+    @GuardedBy("mService")
+    void setWaitingToKill(String waitingToKill) {
+        mWaitingToKill = waitingToKill;
+    }
+
+    @Override
+    public boolean isRemoved() {
+        return mRemoved;
+    }
+
+    void setRemoved(boolean removed) {
+        mRemoved = removed;
+    }
+
+    @GuardedBy("mService")
+    boolean isDebugging() {
+        return mDebugging;
+    }
+
+    @GuardedBy("mService")
+    void setDebugging(boolean debugging) {
+        mDebugging = debugging;
+        mWindowProcessController.setDebugging(debugging);
+    }
+
+    @GuardedBy("mProcLock")
+    boolean hasWaitedForDebugger() {
+        return mWaitedForDebugger;
+    }
+
+    @GuardedBy("mProcLock")
+    void setWaitedForDebugger(boolean waitedForDebugger) {
+        mWaitedForDebugger = waitedForDebugger;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    long getLastActivityTime() {
+        return mLastActivityTime;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setLastActivityTime(long lastActivityTime) {
+        mLastActivityTime = lastActivityTime;
+    }
+
+    @GuardedBy("mService")
+    boolean isUsingWrapper() {
+        return mUsingWrapper;
+    }
+
+    @GuardedBy("mService")
+    void setUsingWrapper(boolean usingWrapper) {
+        mUsingWrapper = usingWrapper;
+        mWindowProcessController.setUsingWrapper(usingWrapper);
+    }
+
+    @GuardedBy("mService")
+    int getLruSeq() {
+        return mLruSeq;
+    }
+
+    @GuardedBy("mService")
+    void setLruSeq(int lruSeq) {
+        mLruSeq = lruSeq;
+    }
+
+    @GuardedBy("mService")
+    String getIsolatedEntryPoint() {
+        return mIsolatedEntryPoint;
+    }
+
+    @GuardedBy("mService")
+    void setIsolatedEntryPoint(String isolatedEntryPoint) {
+        mIsolatedEntryPoint = isolatedEntryPoint;
+    }
+
+    @GuardedBy("mService")
+    String[] getIsolatedEntryPointArgs() {
+        return mIsolatedEntryPointArgs;
+    }
+
+    @GuardedBy("mService")
+    void setIsolatedEntryPointArgs(String[] isolatedEntryPointArgs) {
+        mIsolatedEntryPointArgs = isolatedEntryPointArgs;
+    }
+
+    @GuardedBy("mService")
+    boolean isInFullBackup() {
+        return mInFullBackup;
+    }
+
+    @GuardedBy("mService")
+    void setInFullBackup(boolean inFullBackup) {
+        mInFullBackup = inFullBackup;
     }
 
     @Override
     public boolean isCached() {
-        return mCached;
-    }
-
-    int getCacheOomRankerUseCount() {
-        return mCacheOomRankerUseCount;
+        return mState.isCached();
     }
 
     boolean hasActivities() {
@@ -738,6 +894,22 @@
         return mWindowProcessController.hasRecentTasks();
     }
 
+    @GuardedBy({"mService", "mProcLock"})
+    boolean onCleanupApplicationRecordLSP(ProcessStatsService processStats, boolean allowRestart) {
+        mErrorState.onCleanupApplicationRecordLSP();
+
+        resetPackageList(processStats);
+        unlinkDeathRecipient();
+        makeInactive(processStats);
+        setWaitingToKill(null);
+
+        mState.onCleanupApplicationRecordLSP();
+        mServices.onCleanupApplicationRecordLocked();
+        mReceivers.onCleanupApplicationRecordLocked();
+
+        return mProviders.onCleanupApplicationRecordLocked(allowRestart);
+    }
+
     /**
      * This method returns true if any of the activities within the process record are interesting
      * to the user. See HistoryRecord.isInterestingToUserLocked()
@@ -747,74 +919,26 @@
             return true;
         }
 
-        final int servicesSize = mServices.size();
-        for (int i = 0; i < servicesSize; i++) {
-            ServiceRecord r = mServices.valueAt(i);
-            if (r.isForeground) {
-                return true;
-            }
-        }
-        return false;
+        return mServices.hasForegroundServices();
     }
 
-    public void unlinkDeathRecipient() {
-        if (deathRecipient != null && thread != null) {
-            thread.asBinder().unlinkToDeath(deathRecipient, 0);
-        }
-        deathRecipient = null;
-    }
-
-    void updateHasAboveClientLocked() {
-        hasAboveClient = false;
-        for (int i=connections.size()-1; i>=0; i--) {
-            ConnectionRecord cr = connections.valueAt(i);
-            if ((cr.flags&Context.BIND_ABOVE_CLIENT) != 0) {
-                hasAboveClient = true;
-                break;
-            }
-        }
-    }
-
-    int modifyRawOomAdj(int adj) {
-        if (hasAboveClient) {
-            // If this process has bound to any services with BIND_ABOVE_CLIENT,
-            // then we need to drop its adjustment to be lower than the service's
-            // in order to honor the request.  We want to drop it by one adjustment
-            // level...  but there is special meaning applied to various levels so
-            // we will skip some of them.
-            if (adj < ProcessList.FOREGROUND_APP_ADJ) {
-                // System process will not get dropped, ever
-            } else if (adj < ProcessList.VISIBLE_APP_ADJ) {
-                adj = ProcessList.VISIBLE_APP_ADJ;
-            } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-            } else if (adj < ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
-                adj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
-            } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
-                adj = ProcessList.CACHED_APP_MIN_ADJ;
-            } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
-                adj++;
-            }
-        }
-        return adj;
-    }
-
-    void scheduleCrash(String message) {
+    @GuardedBy("mService")
+    void scheduleCrashLocked(String message) {
         // Checking killedbyAm should keep it from showing the crash dialog if the process
         // was already dead for a good / normal reason.
-        if (!killedByAm) {
-            if (thread != null) {
-                if (pid == Process.myPid()) {
+        if (!mKilledByAm) {
+            if (mThread != null) {
+                if (mPid == Process.myPid()) {
                     Slog.w(TAG, "scheduleCrash: trying to crash system process!");
                     return;
                 }
                 final long ident = Binder.clearCallingIdentity();
                 try {
-                    thread.scheduleCrash(message);
+                    mThread.scheduleCrash(message);
                 } catch (RemoteException e) {
                     // If it's already dead our work is done. If it's wedged just kill it.
                     // We won't get the crash dialog or the error reporting.
-                    kill("scheduleCrash for '" + message + "' failed",
+                    killLocked("scheduleCrash for '" + message + "' failed",
                             ApplicationExitInfo.REASON_CRASH, true);
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -823,30 +947,36 @@
         }
     }
 
-    void kill(String reason, @Reason int reasonCode, boolean noisy) {
-        kill(reason, reasonCode, ApplicationExitInfo.SUBREASON_UNKNOWN, noisy);
+    @GuardedBy("mService")
+    void killLocked(String reason, @Reason int reasonCode, boolean noisy) {
+        killLocked(reason, reasonCode, ApplicationExitInfo.SUBREASON_UNKNOWN, noisy);
     }
 
-    void kill(String reason, @Reason int reasonCode, @SubReason int subReason, boolean noisy) {
-        if (!killedByAm) {
+    @GuardedBy("mService")
+    void killLocked(String reason, @Reason int reasonCode, @SubReason int subReason,
+            boolean noisy) {
+        if (!mKilledByAm) {
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
             if (mService != null && (noisy || info.uid == mService.mCurOomAdjUid)) {
                 mService.reportUidInfoMessageLocked(TAG,
-                        "Killing " + toShortString() + " (adj " + setAdj + "): " + reason,
-                        info.uid);
+                        "Killing " + toShortString() + " (adj " + mState.getSetAdj()
+                        + "): " + reason, info.uid);
             }
-            if (pid > 0) {
+            if (mPid > 0) {
                 mService.mProcessList.noteAppKill(this, reasonCode, subReason, reason);
-                EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
-                Process.killProcessQuiet(pid);
-                ProcessList.killProcessGroup(uid, pid);
+                EventLog.writeEvent(EventLogTags.AM_KILL,
+                        userId, mPid, processName, mState.getSetAdj(), reason);
+                Process.killProcessQuiet(mPid);
+                ProcessList.killProcessGroup(uid, mPid);
             } else {
-                pendingStart = false;
+                mPendingStart = false;
             }
             if (!mPersistent) {
-                killed = true;
-                killedByAm = true;
-                mKillTime = SystemClock.uptimeMillis();
+                synchronized (mProcLock) {
+                    mKilled = true;
+                    mKilledByAm = true;
+                    mKillTime = SystemClock.uptimeMillis();
+                }
             }
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         }
@@ -859,7 +989,7 @@
 
     public void dumpDebug(ProtoOutputStream proto, long fieldId, int lruIndex) {
         long token = proto.start(fieldId);
-        proto.write(ProcessRecordProto.PID, pid);
+        proto.write(ProcessRecordProto.PID, mPid);
         proto.write(ProcessRecordProto.PROCESS_NAME, processName);
         proto.write(ProcessRecordProto.UID, info.uid);
         if (UserHandle.getAppId(info.uid) >= Process.FIRST_APPLICATION_UID) {
@@ -877,16 +1007,17 @@
     }
 
     public String toShortString() {
+        final String shortStringName = mShortStringName;
         if (shortStringName != null) {
             return shortStringName;
         }
         StringBuilder sb = new StringBuilder(128);
         toShortString(sb);
-        return shortStringName = sb.toString();
+        return mShortStringName = sb.toString();
     }
 
     void toShortString(StringBuilder sb) {
-        sb.append(pid);
+        sb.append(mPid);
         sb.append(':');
         sb.append(processName);
         sb.append('/');
@@ -911,6 +1042,7 @@
     }
 
     public String toString() {
+        final String stringName = mStringName;
         if (stringName != null) {
             return stringName;
         }
@@ -920,33 +1052,7 @@
         sb.append(' ');
         toShortString(sb);
         sb.append('}');
-        return stringName = sb.toString();
-    }
-
-    public String makeAdjReason() {
-        if (adjSource != null || adjTarget != null) {
-            StringBuilder sb = new StringBuilder(128);
-            sb.append(' ');
-            if (adjTarget instanceof ComponentName) {
-                sb.append(((ComponentName)adjTarget).flattenToShortString());
-            } else if (adjTarget != null) {
-                sb.append(adjTarget.toString());
-            } else {
-                sb.append("{null}");
-            }
-            sb.append("<=");
-            if (adjSource instanceof ProcessRecord) {
-                sb.append("Proc{");
-                sb.append(((ProcessRecord)adjSource).toShortString());
-                sb.append("}");
-            } else if (adjSource != null) {
-                sb.append(adjSource.toString());
-            } else {
-                sb.append("{null}");
-            }
-            return sb.toString();
-        }
-        return null;
+        return mStringName = sb.toString();
     }
 
     /*
@@ -976,29 +1082,6 @@
         return false;
     }
 
-    public int getSetAdjWithServices() {
-        if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
-            if (hasStartedServices) {
-                return ProcessList.SERVICE_B_ADJ;
-            }
-        }
-        return setAdj;
-    }
-
-    public void forceProcessStateUpTo(int newState) {
-        if (mRepProcState > newState) {
-            mRepProcState = newState;
-            setCurProcState(newState);
-            setCurRawProcState(newState);
-            getPkgList().forEachPackage((pkgName, holder) ->
-                    FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                        uid, processName, pkgName,
-                        ActivityManager.processStateAmToProto(mRepProcState),
-                        holder.appVersion)
-            );
-        }
-    }
-
     /*
      *  Delete all packages from list except the package indicated in info
      */
@@ -1054,194 +1137,6 @@
         return mWindowProcessController;
     }
 
-    void setCurrentSchedulingGroup(int curSchedGroup) {
-        mCurSchedGroup = curSchedGroup;
-        mWindowProcessController.setCurrentSchedulingGroup(curSchedGroup);
-    }
-
-    int getCurrentSchedulingGroup() {
-        return mCurSchedGroup;
-    }
-
-    void setCurProcState(int curProcState) {
-        mCurProcState = curProcState;
-        mWindowProcessController.setCurrentProcState(mCurProcState);
-    }
-
-    int getCurProcState() {
-        return mCurProcState;
-    }
-
-    void setCurRawProcState(int curRawProcState) {
-        mCurRawProcState = curRawProcState;
-    }
-
-    int getCurRawProcState() {
-        return mCurRawProcState;
-    }
-
-    void setReportedProcState(int repProcState) {
-        mRepProcState = repProcState;
-        getPkgList().forEachPackage((pkgName, holder) ->
-                FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                    uid, processName, pkgName,
-                    ActivityManager.processStateAmToProto(mRepProcState),
-                    holder.appVersion)
-        );
-        mWindowProcessController.setReportedProcState(repProcState);
-    }
-
-    int getReportedProcState() {
-        return mRepProcState;
-    }
-
-    void setCrashing(boolean crashing) {
-        mCrashing = crashing;
-        mWindowProcessController.setCrashing(crashing);
-    }
-
-    boolean isCrashing() {
-        return mCrashing;
-    }
-
-    void setNotResponding(boolean notResponding) {
-        mNotResponding = notResponding;
-        mWindowProcessController.setNotResponding(notResponding);
-    }
-
-    boolean isNotResponding() {
-        return mNotResponding;
-    }
-
-    void setPersistent(boolean persistent) {
-        mPersistent = persistent;
-        mWindowProcessController.setPersistent(persistent);
-    }
-
-    boolean isPersistent() {
-        return mPersistent;
-    }
-
-    public void setRequiredAbi(String requiredAbi) {
-        mRequiredAbi = requiredAbi;
-        mWindowProcessController.setRequiredAbi(requiredAbi);
-    }
-
-    String getRequiredAbi() {
-        return mRequiredAbi;
-    }
-
-    void setHasForegroundServices(boolean hasForegroundServices, int fgServiceTypes) {
-        mHasForegroundServices = hasForegroundServices;
-        mFgServiceTypes = fgServiceTypes;
-        mWindowProcessController.setHasForegroundServices(hasForegroundServices);
-    }
-
-    boolean hasForegroundServices() {
-        return mHasForegroundServices;
-    }
-
-    boolean hasLocationForegroundServices() {
-        return mHasForegroundServices
-                && (mFgServiceTypes & ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION) != 0;
-    }
-
-    boolean hasLocationCapability() {
-        return (setCapability & ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0;
-    }
-
-    int getForegroundServiceTypes() {
-        return mHasForegroundServices ? mFgServiceTypes : 0;
-    }
-
-    int getReportedForegroundServiceTypes() {
-        return mRepFgServiceTypes;
-    }
-
-    void setReportedForegroundServiceTypes(int foregroundServiceTypes) {
-        mRepFgServiceTypes = foregroundServiceTypes;
-    }
-
-    void setHasForegroundActivities(boolean hasForegroundActivities) {
-        mHasForegroundActivities = hasForegroundActivities;
-    }
-
-    boolean hasForegroundActivities() {
-        return mHasForegroundActivities;
-    }
-
-    void setHasClientActivities(boolean hasClientActivities) {
-        mHasClientActivities = hasClientActivities;
-        mWindowProcessController.setHasClientActivities(hasClientActivities);
-    }
-
-    boolean hasClientActivities() {
-        return mHasClientActivities;
-    }
-
-    void setHasTopUi(boolean hasTopUi) {
-        mHasTopUi = hasTopUi;
-        mWindowProcessController.setHasTopUi(hasTopUi);
-    }
-
-    boolean hasTopUi() {
-        return mHasTopUi;
-    }
-
-    void setHasOverlayUi(boolean hasOverlayUi) {
-        mHasOverlayUi = hasOverlayUi;
-        mWindowProcessController.setHasOverlayUi(hasOverlayUi);
-    }
-
-    boolean hasOverlayUi() {
-        return mHasOverlayUi;
-    }
-
-    void setInteractionEventTime(long interactionEventTime) {
-        mInteractionEventTime = interactionEventTime;
-        mWindowProcessController.setInteractionEventTime(interactionEventTime);
-    }
-
-    long getInteractionEventTime() {
-        return mInteractionEventTime;
-    }
-
-    void setFgInteractionTime(long fgInteractionTime) {
-        mFgInteractionTime = fgInteractionTime;
-        mWindowProcessController.setFgInteractionTime(fgInteractionTime);
-    }
-
-    long getFgInteractionTime() {
-        return mFgInteractionTime;
-    }
-
-    void setWhenUnimportant(long whenUnimportant) {
-        mWhenUnimportant = whenUnimportant;
-        mWindowProcessController.setWhenUnimportant(whenUnimportant);
-    }
-
-    long getWhenUnimportant() {
-        return mWhenUnimportant;
-    }
-
-    void setDebugging(boolean debugging) {
-        mDebugging = debugging;
-        mWindowProcessController.setDebugging(debugging);
-    }
-
-    boolean isDebugging() {
-        return mDebugging;
-    }
-
-    void setUsingWrapper(boolean usingWrapper) {
-        mUsingWrapper = usingWrapper;
-        mWindowProcessController.setUsingWrapper(usingWrapper);
-    }
-
-    boolean isUsingWrapper() {
-        return mUsingWrapper;
-    }
-
     /**
      * Allows background activity starts using token {@param entity}. Optionally, you can provide
      * {@param originatingToken} if you have one such originating token, this is useful for tracing
@@ -1259,75 +1154,6 @@
         mWindowProcessController.removeAllowBackgroundActivityStartsToken(entity);
     }
 
-    void addBoundClientUid(int clientUid) {
-        mBoundClientUids.add(clientUid);
-        mWindowProcessController.setBoundClientUids(mBoundClientUids);
-    }
-
-    void updateBoundClientUids() {
-        if (mServices.isEmpty()) {
-            clearBoundClientUids();
-            return;
-        }
-        // grab a set of clientUids of all connections of all services
-        ArraySet<Integer> boundClientUids = new ArraySet<>();
-        final int serviceCount = mServices.size();
-        for (int j = 0; j < serviceCount; j++) {
-            ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns =
-                    mServices.valueAt(j).getConnections();
-            final int N = conns.size();
-            for (int conni = 0; conni < N; conni++) {
-                ArrayList<ConnectionRecord> c = conns.valueAt(conni);
-                for (int i = 0; i < c.size(); i++) {
-                    boundClientUids.add(c.get(i).clientUid);
-                }
-            }
-        }
-        mBoundClientUids = boundClientUids;
-        mWindowProcessController.setBoundClientUids(mBoundClientUids);
-    }
-
-    void addBoundClientUidsOfNewService(ServiceRecord sr) {
-        if (sr == null) {
-            return;
-        }
-        ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns = sr.getConnections();
-        for (int conni = conns.size() - 1; conni >= 0; conni--) {
-            ArrayList<ConnectionRecord> c = conns.valueAt(conni);
-            for (int i = 0; i < c.size(); i++) {
-                mBoundClientUids.add(c.get(i).clientUid);
-            }
-        }
-        mWindowProcessController.setBoundClientUids(mBoundClientUids);
-    }
-
-    void clearBoundClientUids() {
-        mBoundClientUids.clear();
-        mWindowProcessController.setBoundClientUids(mBoundClientUids);
-    }
-
-    void setActiveInstrumentation(ActiveInstrumentation instr) {
-        mInstr = instr;
-        boolean isInstrumenting = instr != null;
-        mWindowProcessController.setInstrumenting(
-                isInstrumenting,
-                isInstrumenting ? instr.mSourceUid : -1,
-                isInstrumenting && instr.mHasBackgroundActivityStartsPermission);
-    }
-
-    ActiveInstrumentation getActiveInstrumentation() {
-        return mInstr;
-    }
-
-    void setCurRawAdj(int curRawAdj) {
-        mCurRawAdj = curRawAdj;
-        mWindowProcessController.setPerceptible(curRawAdj <= ProcessList.PERCEPTIBLE_APP_ADJ);
-    }
-
-    int getCurRawAdj() {
-        return mCurRawAdj;
-    }
-
     @Override
     public void clearProfilerIfNeeded() {
         synchronized (mService.mAppProfiler.mProfilerLock) {
@@ -1338,13 +1164,13 @@
     @Override
     public void updateServiceConnectionActivities() {
         synchronized (mService) {
-            mService.mServices.updateServiceConnectionActivitiesLocked(this);
+            mService.mServices.updateServiceConnectionActivitiesLocked(mServices);
         }
     }
 
     @Override
     public void setPendingUiClean(boolean pendingUiClean) {
-        synchronized (mService) {
+        synchronized (mProcLock) {
             mProfile.setPendingUiClean(pendingUiClean);
         }
     }
@@ -1353,7 +1179,7 @@
     public void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
         synchronized (mService) {
             setPendingUiClean(true);
-            forceProcessStateUpTo(newState);
+            mState.forceProcessStateUpTo(newState);
         }
     }
 
@@ -1362,40 +1188,35 @@
             boolean updateOomAdj) {
         synchronized (mService) {
             if (updateServiceConnectionActivities) {
-                mService.mServices.updateServiceConnectionActivitiesLocked(this);
+                mService.mServices.updateServiceConnectionActivitiesLocked(mServices);
             }
-            if (thread == null) {
+            if (mThread == null) {
                 // Only update lru and oom-adj if the process is alive. Because it may be called
                 // when cleaning up the last activity from handling process died, the dead process
                 // should not be added to lru list again.
                 return;
             }
-            mService.mProcessList.updateLruProcessLocked(this, activityChange, null /* client */);
+            mService.updateLruProcessLocked(this, activityChange, null /* client */);
             if (updateOomAdj) {
                 mService.updateOomAdjLocked(this, OomAdjuster.OOM_ADJ_REASON_ACTIVITY);
             }
         }
     }
 
-    @Override
-    public boolean isRemoved() {
-        return removed;
-    }
-
     /**
      * Returns the total time (in milliseconds) spent executing in both user and system code.
      * Safe to call without lock held.
      */
     @Override
     public long getCpuTime() {
-        return mService.mAppProfiler.getCpuTimeForPid(pid);
+        return mService.mAppProfiler.getCpuTimeForPid(mPid);
     }
 
     @Override
     public void onStartActivity(int topProcessState, boolean setProfileProc, String packageName,
             long versionCode) {
         synchronized (mService) {
-            waitingToKill = null;
+            mWaitingToKill = null;
             if (setProfileProc) {
                 synchronized (mService.mAppProfiler.mProfilerLock) {
                     mService.mAppProfiler.setProfileProcLPf(this);
@@ -1408,9 +1229,9 @@
             // Update oom adj first, we don't want the additional states are involved in this round.
             updateProcessInfo(false /* updateServiceConnectionActivities */,
                     true /* activityChange */, true /* updateOomAdj */);
-            hasShownUi = true;
             setPendingUiClean(true);
-            forceProcessStateUpTo(topProcessState);
+            mState.setHasShownUi(true);
+            mState.forceProcessStateUpTo(topProcessState);
         }
     }
 
@@ -1423,20 +1244,12 @@
 
     @Override
     public void setRunningRemoteAnimation(boolean runningRemoteAnimation) {
-        if (pid == Process.myPid()) {
+        if (mPid == Process.myPid()) {
             Slog.wtf(TAG, "system can't run remote animation");
             return;
         }
         synchronized (mService) {
-            if (this.runningRemoteAnimation == runningRemoteAnimation) {
-                return;
-            }
-            this.runningRemoteAnimation = runningRemoteAnimation;
-            if (DEBUG_OOM_ADJ) {
-                Slog.i(TAG, "Setting runningRemoteAnimation=" + runningRemoteAnimation
-                        + " for pid=" + pid);
-            }
-            mService.updateOomAdjLocked(this, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
+            mState.setRunningRemoteAnimation(runningRemoteAnimation);
         }
     }
 
@@ -1445,7 +1258,7 @@
     }
 
     public int getProcessClassEnum() {
-        if (pid == MY_PID) {
+        if (mPid == MY_PID) {
             return ServerProtoEnums.SYSTEM_SERVER;
         }
         if (info == null) {
@@ -1455,687 +1268,9 @@
             ServerProtoEnums.DATA_APP;
     }
 
-    /**
-     * Unless configured otherwise, swallow ANRs in background processes & kill the process.
-     * Non-private access is for tests only.
-     */
-    @VisibleForTesting
-    boolean isSilentAnr() {
-        return !getShowBackground() && !isInterestingForBackgroundTraces();
-    }
-
     /** Non-private access is for tests only. */
     @VisibleForTesting
     List<ProcessRecord> getLruProcessList() {
-        return mService.mProcessList.mLruProcesses;
-    }
-
-    /** Non-private access is for tests only. */
-    @VisibleForTesting
-    boolean isMonitorCpuUsage() {
-        return mService.mAppProfiler.MONITOR_CPU_USAGE;
-    }
-
-    void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
-            String parentShortComponentName, WindowProcessController parentProcess,
-            boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
-        ArrayList<Integer> firstPids = new ArrayList<>(5);
-        SparseArray<Boolean> lastPids = new SparseArray<>(20);
-
-        mWindowProcessController.appEarlyNotResponding(annotation, () -> kill("anr",
-                  ApplicationExitInfo.REASON_ANR, true));
-
-        long anrTime = SystemClock.uptimeMillis();
-        if (isMonitorCpuUsage()) {
-            mService.updateCpuStatsNow();
-        }
-
-        final boolean isSilentAnr;
-        synchronized (mService) {
-            // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
-            if (mService.mAtmInternal.isShuttingDown()) {
-                Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
-                return;
-            } else if (isNotResponding()) {
-                Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
-                return;
-            } else if (isCrashing()) {
-                Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
-                return;
-            } else if (killedByAm) {
-                Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
-                return;
-            } else if (killed) {
-                Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
-                return;
-            }
-
-            // In case we come through here for the same app before completing
-            // this one, mark as anring now so we will bail out.
-            setNotResponding(true);
-
-            // Log the ANR to the event log.
-            EventLog.writeEvent(EventLogTags.AM_ANR, userId, pid, processName, info.flags,
-                    annotation);
-
-            // Dump thread traces as quickly as we can, starting with "interesting" processes.
-            firstPids.add(pid);
-
-            // Don't dump other PIDs if it's a background ANR or is requested to only dump self.
-            isSilentAnr = isSilentAnr();
-            if (!isSilentAnr && !onlyDumpSelf) {
-                int parentPid = pid;
-                if (parentProcess != null && parentProcess.getPid() > 0) {
-                    parentPid = parentProcess.getPid();
-                }
-                if (parentPid != pid) firstPids.add(parentPid);
-
-                if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
-
-                for (int i = getLruProcessList().size() - 1; i >= 0; i--) {
-                    ProcessRecord r = getLruProcessList().get(i);
-                    if (r != null && r.thread != null) {
-                        int myPid = r.pid;
-                        if (myPid > 0 && myPid != pid && myPid != parentPid && myPid != MY_PID) {
-                            if (r.isPersistent()) {
-                                firstPids.add(myPid);
-                                if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
-                            } else if (r.treatLikeActivity) {
-                                firstPids.add(myPid);
-                                if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
-                            } else {
-                                lastPids.put(myPid, Boolean.TRUE);
-                                if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // Check if package is still being loaded
-        boolean isPackageLoading = false;
-        final PackageManagerInternal packageManagerInternal =
-                mService.getPackageManagerInternal();
-        if (aInfo != null && aInfo.packageName != null) {
-            IncrementalStatesInfo incrementalStatesInfo =
-                    packageManagerInternal.getIncrementalStatesInfo(
-                            aInfo.packageName, uid, userId);
-            if (incrementalStatesInfo != null) {
-                isPackageLoading = incrementalStatesInfo.isLoading();
-            }
-        }
-
-        // Log the ANR to the main log.
-        StringBuilder info = new StringBuilder();
-        info.setLength(0);
-        info.append("ANR in ").append(processName);
-        if (activityShortComponentName != null) {
-            info.append(" (").append(activityShortComponentName).append(")");
-        }
-        info.append("\n");
-        info.append("PID: ").append(pid).append("\n");
-        if (annotation != null) {
-            info.append("Reason: ").append(annotation).append("\n");
-        }
-        if (parentShortComponentName != null
-                && parentShortComponentName.equals(activityShortComponentName)) {
-            info.append("Parent: ").append(parentShortComponentName).append("\n");
-        }
-
-        if (isPackageLoading) {
-            // Report in the main log that the package is still loading
-            final float loadingProgress = packageManagerInternal.getIncrementalStatesInfo(
-                    aInfo.packageName, uid, userId).getProgress();
-            info.append("Package is ").append((int) (loadingProgress * 100)).append("% loaded.\n");
-        }
-
-        StringBuilder report = new StringBuilder();
-        report.append(MemoryPressureUtil.currentPsiState());
-        ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
-
-        // don't dump native PIDs for background ANRs unless it is the process of interest
-        String[] nativeProcs = null;
-        if (isSilentAnr || onlyDumpSelf) {
-            for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
-                if (NATIVE_STACKS_OF_INTEREST[i].equals(processName)) {
-                    nativeProcs = new String[] { processName };
-                    break;
-                }
-            }
-        } else {
-            nativeProcs = NATIVE_STACKS_OF_INTEREST;
-        }
-
-        int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);
-        ArrayList<Integer> nativePids = null;
-
-        if (pids != null) {
-            nativePids = new ArrayList<>(pids.length);
-            for (int i : pids) {
-                nativePids.add(i);
-            }
-        }
-
-        // For background ANRs, don't pass the ProcessCpuTracker to
-        // avoid spending 1/2 second collecting stats to rank lastPids.
-        StringWriter tracesFileException = new StringWriter();
-        // To hold the start and end offset to the ANR trace file respectively.
-        final long[] offsets = new long[2];
-        File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
-                isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
-                nativePids, tracesFileException, offsets);
-
-        if (isMonitorCpuUsage()) {
-            mService.updateCpuStatsNow();
-            mService.mAppProfiler.printCurrentCpuState(report, anrTime);
-            info.append(processCpuTracker.printCurrentLoad());
-            info.append(report);
-        }
-        report.append(tracesFileException.getBuffer());
-
-        info.append(processCpuTracker.printCurrentState(anrTime));
-
-        Slog.e(TAG, info.toString());
-        if (tracesFile == null) {
-            // There is no trace file, so dump (only) the alleged culprit's threads to the log
-            Process.sendSignal(pid, Process.SIGNAL_QUIT);
-        } else if (offsets[1] > 0) {
-            // We've dumped into the trace file successfully
-            mService.mProcessList.mAppExitInfoTracker.scheduleLogAnrTrace(
-                    pid, uid, getPackageList(), tracesFile, offsets[0], offsets[1]);
-        }
-
-        FrameworkStatsLog.write(FrameworkStatsLog.ANR_OCCURRED, uid, processName,
-                activityShortComponentName == null ? "unknown": activityShortComponentName,
-                annotation,
-                (this.info != null) ? (this.info.isInstantApp()
-                        ? FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE
-                        : FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE)
-                        : FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE,
-                isInterestingToUserLocked()
-                        ? FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
-                        : FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND,
-                getProcessClassEnum(),
-                (this.info != null) ? this.info.packageName : "", isPackageLoading);
-        final ProcessRecord parentPr = parentProcess != null
-                ? (ProcessRecord) parentProcess.mOwner : null;
-        mService.addErrorToDropBox("anr", this, processName, activityShortComponentName,
-                parentShortComponentName, parentPr, annotation, report.toString(), tracesFile,
-                null);
-
-        if (mWindowProcessController.appNotResponding(info.toString(), () -> kill("anr",
-                ApplicationExitInfo.REASON_ANR, true),
-                () -> {
-                    synchronized (mService) {
-                        mService.mServices.scheduleServiceTimeoutLocked(this);
-                    }
-                })) {
-            return;
-        }
-
-        synchronized (mService) {
-            // mBatteryStatsService can be null if the AMS is constructed with injector only. This
-            // will only happen in tests.
-            if (mService.mBatteryStatsService != null) {
-                mService.mBatteryStatsService.noteProcessAnr(processName, uid);
-            }
-
-            if (isSilentAnr() && !isDebugging()) {
-                kill("bg anr", ApplicationExitInfo.REASON_ANR, true);
-                return;
-            }
-
-            // Set the app's notResponding state, and look up the errorReportReceiver
-            makeAppNotRespondingLocked(activityShortComponentName,
-                    annotation != null ? "ANR " + annotation : "ANR", info.toString());
-
-            // Notify package manager service to possibly update package state
-            if (aInfo != null && aInfo.packageName != null) {
-                packageManagerInternal.notifyPackageCrashOrAnr(aInfo.packageName);
-            }
-
-            // mUiHandler can be null if the AMS is constructed with injector only. This will only
-            // happen in tests.
-            if (mService.mUiHandler != null) {
-                // Bring up the infamous App Not Responding dialog
-                Message msg = Message.obtain();
-                msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
-                msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);
-
-                mService.mUiHandler.sendMessage(msg);
-            }
-        }
-    }
-
-    private void makeAppNotRespondingLocked(String activity, String shortMsg, String longMsg) {
-        setNotResponding(true);
-        // mAppErrors can be null if the AMS is constructed with injector only. This will only
-        // happen in tests.
-        if (mService.mAppErrors != null) {
-            notRespondingReport = mService.mAppErrors.generateProcessError(this,
-                    ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
-                    activity, shortMsg, longMsg, null);
-        }
-        startAppProblemLocked();
-        getWindowProcessController().stopFreezingActivities();
-    }
-
-    void startAppProblemLocked() {
-        // If this app is not running under the current user, then we can't give it a report button
-        // because that would require launching the report UI under a different user.
-        errorReportReceiver = null;
-
-        for (int userId : mService.mUserController.getCurrentProfileIds()) {
-            if (this.userId == userId) {
-                errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
-                        mService.mContext, info.packageName, info.flags);
-            }
-        }
-        mService.skipCurrentReceiverLocked(this);
-    }
-
-    private boolean isInterestingForBackgroundTraces() {
-        // The system_server is always considered interesting.
-        if (pid == MY_PID) {
-            return true;
-        }
-
-        // A package is considered interesting if any of the following is true :
-        //
-        // - It's displaying an activity.
-        // - It's the SystemUI.
-        // - It has an overlay or a top UI visible.
-        //
-        // NOTE: The check whether a given ProcessRecord belongs to the systemui
-        // process is a bit of a kludge, but the same pattern seems repeated at
-        // several places in the system server.
-        return isInterestingToUserLocked() ||
-                (info != null && "com.android.systemui".equals(info.packageName))
-                || (hasTopUi() || hasOverlayUi());
-    }
-
-    private boolean getShowBackground() {
-        return Settings.Secure.getInt(mService.mContext.getContentResolver(),
-                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
-    }
-
-    void resetCachedInfo() {
-        mCachedHasActivities = VALUE_INVALID;
-        mCachedIsHeavyWeight = VALUE_INVALID;
-        mCachedHasVisibleActivities = VALUE_INVALID;
-        mCachedIsHomeProcess = VALUE_INVALID;
-        mCachedIsPreviousProcess = VALUE_INVALID;
-        mCachedHasRecentTasks = VALUE_INVALID;
-        mCachedIsReceivingBroadcast = VALUE_INVALID;
-        mCachedAdj = ProcessList.INVALID_ADJ;
-        mCachedForegroundActivities = false;
-        mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-        mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-    }
-
-    boolean getCachedHasActivities() {
-        if (mCachedHasActivities == VALUE_INVALID) {
-            mCachedHasActivities = getWindowProcessController().hasActivities() ? VALUE_TRUE
-                    : VALUE_FALSE;
-        }
-        return mCachedHasActivities == VALUE_TRUE;
-    }
-
-    boolean getCachedIsHeavyWeight() {
-        if (mCachedIsHeavyWeight == VALUE_INVALID) {
-            mCachedIsHeavyWeight = getWindowProcessController().isHeavyWeightProcess()
-                    ? VALUE_TRUE : VALUE_FALSE;
-        }
-        return mCachedIsHeavyWeight == VALUE_TRUE;
-    }
-
-    boolean getCachedHasVisibleActivities() {
-        if (mCachedHasVisibleActivities == VALUE_INVALID) {
-            mCachedHasVisibleActivities = getWindowProcessController().hasVisibleActivities()
-                    ? VALUE_TRUE : VALUE_FALSE;
-        }
-        return mCachedHasVisibleActivities == VALUE_TRUE;
-    }
-
-    boolean getCachedIsHomeProcess() {
-        if (mCachedIsHomeProcess == VALUE_INVALID) {
-            if (getWindowProcessController().isHomeProcess()) {
-                mCachedIsHomeProcess = VALUE_TRUE;
-                mService.mAppProfiler.mHasHomeProcess = true;
-            } else {
-                mCachedIsHomeProcess = VALUE_FALSE;
-            }
-        }
-        return mCachedIsHomeProcess == VALUE_TRUE;
-    }
-
-    boolean getCachedIsPreviousProcess() {
-        if (mCachedIsPreviousProcess == VALUE_INVALID) {
-            if (getWindowProcessController().isPreviousProcess()) {
-                mCachedIsPreviousProcess = VALUE_TRUE;
-                mService.mAppProfiler.mHasPreviousProcess = true;
-            } else {
-                mCachedIsPreviousProcess = VALUE_FALSE;
-            }
-        }
-        return mCachedIsPreviousProcess == VALUE_TRUE;
-    }
-
-    boolean getCachedHasRecentTasks() {
-        if (mCachedHasRecentTasks == VALUE_INVALID) {
-            mCachedHasRecentTasks = getWindowProcessController().hasRecentTasks()
-                    ? VALUE_TRUE : VALUE_FALSE;
-        }
-        return mCachedHasRecentTasks == VALUE_TRUE;
-    }
-
-    boolean getCachedIsReceivingBroadcast(ArraySet<BroadcastQueue> tmpQueue) {
-        if (mCachedIsReceivingBroadcast == VALUE_INVALID) {
-            tmpQueue.clear();
-            mCachedIsReceivingBroadcast = mService.isReceivingBroadcastLocked(this, tmpQueue)
-                    ? VALUE_TRUE : VALUE_FALSE;
-            if (mCachedIsReceivingBroadcast == VALUE_TRUE) {
-                mCachedSchedGroup = tmpQueue.contains(mService.mFgBroadcastQueue)
-                        ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
-            }
-        }
-        return mCachedIsReceivingBroadcast == VALUE_TRUE;
-    }
-
-    void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback,
-            int adj, boolean foregroundActivities, int procState, int schedGroup, int appUid,
-            int logUid, int processCurTop) {
-        if (mCachedAdj != ProcessList.INVALID_ADJ) {
-            return;
-        }
-        callback.initialize(this, adj, foregroundActivities, procState, schedGroup, appUid, logUid,
-                processCurTop);
-        final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX,
-                getWindowProcessController().computeOomAdjFromActivities(callback));
-
-        mCachedAdj = callback.adj;
-        mCachedForegroundActivities = callback.foregroundActivities;
-        mCachedProcState = callback.procState;
-        mCachedSchedGroup = callback.schedGroup;
-
-        if (mCachedAdj == ProcessList.VISIBLE_APP_ADJ) {
-            mCachedAdj += minLayer;
-        }
-    }
-
-    public void addAllowBackgroundFgsStartsToken(Binder entity) {
-        mBackgroundFgsStartTokens.add(entity);
-    }
-
-    public void removeAllowBackgroundFgsStartsToken(Binder entity) {
-        mBackgroundFgsStartTokens.remove(entity);
-    }
-
-    public boolean areBackgroundFgsStartsAllowedByToken() {
-        return !mBackgroundFgsStartTokens.isEmpty();
-    }
-
-    ErrorDialogController getDialogController() {
-        return mDialogController;
-    }
-
-    void resetAllowStartFgs() {
-        mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
-        mAllowStartFgs = mAllowStartFgsByPermission;
-    }
-
-    void bumpAllowStartFgsState(int newProcState) {
-        if (newProcState < mAllowStartFgsState) {
-            mAllowStartFgsState = newProcState;
-        }
-    }
-
-    void setAllowStartFgsByPermission() {
-        boolean ret = false;
-        if (!ret) {
-            boolean isSystem = false;
-            final int uid = UserHandle.getAppId(info.uid);
-            switch (uid) {
-                case ROOT_UID:
-                case SYSTEM_UID:
-                case NFC_UID:
-                case SHELL_UID:
-                    isSystem = true;
-                    break;
-                default:
-                    isSystem = false;
-                    break;
-            }
-
-            if (isSystem) {
-                ret = true;
-            }
-        }
-
-        if (!ret) {
-            for (int i = 0; i < ALLOW_BG_START_FGS_PERMISSIONS.length; ++i) {
-                if (ActivityManager.checkComponentPermission(ALLOW_BG_START_FGS_PERMISSIONS[i],
-                        info.uid, -1, true)
-                        == PERMISSION_GRANTED) {
-                    ret = true;
-                    break;
-                }
-            }
-        }
-        mAllowStartFgs = mAllowStartFgsByPermission = ret;
-    }
-
-    boolean isAllowedStartFgsState() {
-        return mAllowStartFgsState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-    }
-
-    void setAllowStartFgs() {
-        if (mAllowStartFgs) {
-            return;
-        }
-        if (!mAllowStartFgs) {
-            mAllowStartFgs = isAllowedStartFgsState();
-        }
-
-        if (!mAllowStartFgs) {
-            // Is the calling UID a device owner app?
-            if (mService.mInternal != null) {
-                mAllowStartFgs = mService.mInternal.isDeviceOwner(info.uid);
-            }
-        }
-
-        if (!mAllowStartFgs) {
-            if (mService.mInternal != null) {
-                mAllowStartFgs = mService.mInternal.isAssociatedCompanionApp(
-                        UserHandle.getUserId(info.uid), info.uid);
-            }
-        }
-
-        if (!mAllowStartFgs) {
-            // Is the calling UID a profile owner app?
-            if (mService.mInternal != null) {
-                mAllowStartFgs = mService.mInternal.isProfileOwner(info.uid);
-            }
-        }
-
-        if (!mAllowStartFgs) {
-            // uid is on DeviceIdleController's user/system allowlist
-            // or AMS's FgsStartTempAllowList.
-            mAllowStartFgs = mService.isAllowlistedForFgsStartLocked(info.uid);
-        }
-    }
-
-    /** A controller to generate error dialogs in {@link ProcessRecord} */
-    class ErrorDialogController {
-        /** dialogs being displayed due to crash */
-        private List<AppErrorDialog> mCrashDialogs;
-        /** dialogs being displayed due to app not responding */
-        private List<AppNotRespondingDialog> mAnrDialogs;
-        /** dialogs displayed due to strict mode violation */
-        private List<StrictModeViolationDialog> mViolationDialogs;
-        /** current wait for debugger dialog */
-        private AppWaitingForDebuggerDialog mWaitDialog;
-
-        boolean hasCrashDialogs() {
-            return mCrashDialogs != null;
-        }
-
-        boolean hasAnrDialogs() {
-            return mAnrDialogs != null;
-        }
-
-        boolean hasViolationDialogs() {
-            return mViolationDialogs != null;
-        }
-
-        boolean hasDebugWaitingDialog() {
-            return mWaitDialog != null;
-        }
-
-        void clearAllErrorDialogs() {
-            clearCrashDialogs();
-            clearAnrDialogs();
-            clearViolationDialogs();
-            clearWaitingDialog();
-        }
-
-        void clearCrashDialogs() {
-            clearCrashDialogs(true /* needDismiss */);
-        }
-
-        void clearCrashDialogs(boolean needDismiss) {
-            if (mCrashDialogs == null) {
-                return;
-            }
-            if (needDismiss) {
-                forAllDialogs(mCrashDialogs, Dialog::dismiss);
-            }
-            mCrashDialogs = null;
-        }
-
-        void clearAnrDialogs() {
-            if (mAnrDialogs == null) {
-                return;
-            }
-            forAllDialogs(mAnrDialogs, Dialog::dismiss);
-            mAnrDialogs = null;
-        }
-
-        void clearViolationDialogs() {
-            if (mViolationDialogs == null) {
-                return;
-            }
-            forAllDialogs(mViolationDialogs, Dialog::dismiss);
-            mViolationDialogs = null;
-        }
-
-        void clearWaitingDialog() {
-            if (mWaitDialog == null) {
-                return;
-            }
-            mWaitDialog.dismiss();
-            mWaitDialog = null;
-        }
-
-        void forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c) {
-            for (int i = dialogs.size() - 1; i >= 0; i--) {
-                c.accept(dialogs.get(i));
-            }
-        }
-
-        void showCrashDialogs(AppErrorDialog.Data data) {
-            List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
-            mCrashDialogs = new ArrayList<>();
-            for (int i = contexts.size() - 1; i >= 0; i--) {
-                final Context c = contexts.get(i);
-                mCrashDialogs.add(new AppErrorDialog(c, mService, data));
-            }
-            mService.mUiHandler.post(() -> {
-                List<AppErrorDialog> dialogs;
-                synchronized (mService) {
-                    dialogs = mCrashDialogs;
-                }
-                if (dialogs != null) {
-                    forAllDialogs(dialogs, Dialog::show);
-                }
-            });
-        }
-
-        void showAnrDialogs(AppNotRespondingDialog.Data data) {
-            List<Context> contexts = getDisplayContexts(isSilentAnr() /* lastUsedOnly */);
-            mAnrDialogs = new ArrayList<>();
-            for (int i = contexts.size() - 1; i >= 0; i--) {
-                final Context c = contexts.get(i);
-                mAnrDialogs.add(new AppNotRespondingDialog(mService, c, data));
-            }
-            mService.mUiHandler.post(() -> {
-                List<AppNotRespondingDialog> dialogs;
-                synchronized (mService) {
-                    dialogs = mAnrDialogs;
-                }
-                if (dialogs != null) {
-                    forAllDialogs(dialogs, Dialog::show);
-                }
-            });
-        }
-
-        void showViolationDialogs(AppErrorResult res) {
-            List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
-            mViolationDialogs = new ArrayList<>();
-            for (int i = contexts.size() - 1; i >= 0; i--) {
-                final Context c = contexts.get(i);
-                mViolationDialogs.add(
-                        new StrictModeViolationDialog(c, mService, res, ProcessRecord.this));
-            }
-            mService.mUiHandler.post(() -> {
-                List<StrictModeViolationDialog> dialogs;
-                synchronized (mService) {
-                    dialogs = mViolationDialogs;
-                }
-                if (dialogs != null) {
-                    forAllDialogs(dialogs, Dialog::show);
-                }
-            });
-        }
-
-        void showDebugWaitingDialogs() {
-            List<Context> contexts = getDisplayContexts(true /* lastUsedOnly */);
-            final Context c = contexts.get(0);
-            mWaitDialog = new AppWaitingForDebuggerDialog(mService, c, ProcessRecord.this);
-
-            mService.mUiHandler.post(() -> {
-                Dialog dialog;
-                synchronized (mService) {
-                    dialog = mWaitDialog;
-                }
-                if (dialog != null) {
-                    dialog.show();
-                }
-            });
-        }
-
-        /**
-         * Helper function to collect contexts from crashed app located displays
-         *
-         * @param lastUsedOnly Sets to {@code true} to indicate to only get last used context.
-         *                     Sets to {@code false} to collect contexts from crashed app located
-         *                     displays.
-         *
-         * @return display context list
-         */
-        private List<Context> getDisplayContexts(boolean lastUsedOnly) {
-            List<Context> displayContexts = new ArrayList<>();
-            if (!lastUsedOnly) {
-                mWindowProcessController.getDisplayContextsWithErrorDialogs(displayContexts);
-            }
-            // If there is no foreground window display, fallback to last used display.
-            if (displayContexts.isEmpty() || lastUsedOnly) {
-                displayContexts.add(mService.mWmInternal != null
-                        ? mService.mWmInternal.getTopFocusedDisplayUiContext()
-                        : mService.mUiContext);
-            }
-            return displayContexts;
-        }
+        return mService.mProcessList.getLruProcessesLOSP();
     }
 }
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
new file mode 100644
index 0000000..5c3bf60
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2020 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 android.app.ActivityManager;
+import android.content.Context;
+import android.os.IBinder;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * The state info of all services in the process.
+ */
+final class ProcessServiceRecord {
+    /**
+     * Are there any client services with activities?
+     */
+    private boolean mHasClientActivities;
+
+    /**
+     * Running any services that are foreground?
+     */
+    private boolean mHasForegroundServices;
+
+    /**
+     * Service that applied current connectionGroup/Importance.
+     */
+    private ServiceRecord mConnectionService;
+
+    /**
+     * Last group set by a connection.
+     */
+    private int mConnectionGroup;
+
+    /**
+     * Last importance set by a connection.
+     */
+    private int mConnectionImportance;
+
+    /**
+     * Type of foreground service, if there is a foreground service.
+     */
+    private int mFgServiceTypes;
+
+    /**
+     * Last reported foreground service types.
+     */
+    private int mRepFgServiceTypes;
+
+    /**
+     * Bound using BIND_ABOVE_CLIENT, so want to be lower.
+     */
+    private boolean mHasAboveClient;
+
+    /**
+     * Bound using BIND_TREAT_LIKE_ACTIVITY.
+     */
+    private boolean mTreatLikeActivity;
+
+    /**
+     * Do we need to be executing services in the foreground?
+     */
+    private boolean mExecServicesFg;
+
+    /**
+     * App is allowed to manage allowlists such as temporary Power Save mode allowlist.
+     */
+    boolean mAllowlistManager;
+
+    /**
+     * All ServiceRecord running in this process.
+     */
+    private final ArraySet<ServiceRecord> mServices = new ArraySet<>();
+
+    /**
+     * Services that are currently executing code (need to remain foreground).
+     */
+    private final ArraySet<ServiceRecord> mExecutingServices = new ArraySet<>();
+
+    /**
+     * All ConnectionRecord this process holds.
+     */
+    private final ArraySet<ConnectionRecord> mConnections = new ArraySet<>();
+
+    /**
+     * A set of UIDs of all bound clients.
+     */
+    private ArraySet<Integer> mBoundClientUids = new ArraySet<>();
+
+    final ProcessRecord mApp;
+
+    private final ActivityManagerService mService;
+
+    ProcessServiceRecord(ProcessRecord app) {
+        mApp = app;
+        mService = app.mService;
+    }
+
+    void setHasClientActivities(boolean hasClientActivities) {
+        mHasClientActivities = hasClientActivities;
+        mApp.getWindowProcessController().setHasClientActivities(hasClientActivities);
+    }
+
+    boolean hasClientActivities() {
+        return mHasClientActivities;
+    }
+
+    void setHasForegroundServices(boolean hasForegroundServices, int fgServiceTypes) {
+        mHasForegroundServices = hasForegroundServices;
+        mFgServiceTypes = fgServiceTypes;
+        mApp.getWindowProcessController().setHasForegroundServices(hasForegroundServices);
+    }
+
+    boolean hasForegroundServices() {
+        return mHasForegroundServices;
+    }
+
+    int getForegroundServiceTypes() {
+        return mHasForegroundServices ? mFgServiceTypes : 0;
+    }
+
+    int getReportedForegroundServiceTypes() {
+        return mRepFgServiceTypes;
+    }
+
+    void setReportedForegroundServiceTypes(int foregroundServiceTypes) {
+        mRepFgServiceTypes = foregroundServiceTypes;
+    }
+
+    ServiceRecord getConnectionService() {
+        return mConnectionService;
+    }
+
+    void setConnectionService(ServiceRecord connectionService) {
+        mConnectionService = connectionService;
+    }
+
+    int getConnectionGroup() {
+        return mConnectionGroup;
+    }
+
+    void setConnectionGroup(int connectionGroup) {
+        mConnectionGroup = connectionGroup;
+    }
+
+    int getConnectionImportance() {
+        return mConnectionImportance;
+    }
+
+    void setConnectionImportance(int connectionImportance) {
+        mConnectionImportance = connectionImportance;
+    }
+
+    void updateHasAboveClientLocked() {
+        mHasAboveClient = false;
+        for (int i = mConnections.size() - 1; i >= 0; i--) {
+            ConnectionRecord cr = mConnections.valueAt(i);
+            if ((cr.flags & Context.BIND_ABOVE_CLIENT) != 0) {
+                mHasAboveClient = true;
+                break;
+            }
+        }
+    }
+
+    void setHasAboveClient(boolean hasAboveClient) {
+        mHasAboveClient = hasAboveClient;
+    }
+
+    boolean hasAboveClient() {
+        return mHasAboveClient;
+    }
+
+    int modifyRawOomAdj(int adj) {
+        if (mHasAboveClient) {
+            // If this process has bound to any services with BIND_ABOVE_CLIENT,
+            // then we need to drop its adjustment to be lower than the service's
+            // in order to honor the request.  We want to drop it by one adjustment
+            // level...  but there is special meaning applied to various levels so
+            // we will skip some of them.
+            if (adj < ProcessList.FOREGROUND_APP_ADJ) {
+                // System process will not get dropped, ever
+            } else if (adj < ProcessList.VISIBLE_APP_ADJ) {
+                adj = ProcessList.VISIBLE_APP_ADJ;
+            } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+            } else if (adj < ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+                adj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
+            } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
+                adj = ProcessList.CACHED_APP_MIN_ADJ;
+            } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
+                adj++;
+            }
+        }
+        return adj;
+    }
+
+    boolean isTreatedLikeActivity() {
+        return mTreatLikeActivity;
+    }
+
+    void setTreatLikeActivity(boolean treatLikeActivity) {
+        mTreatLikeActivity = treatLikeActivity;
+    }
+
+    boolean shouldExecServicesFg() {
+        return mExecServicesFg;
+    }
+
+    void setExecServicesFg(boolean execServicesFg) {
+        mExecServicesFg = execServicesFg;
+    }
+
+    /**
+     * Records a service as running in the process. Note that this method does not actually start
+     * the service, but records the service as started for bookkeeping.
+     *
+     * @return true if the service was added, false otherwise.
+     */
+    boolean startService(ServiceRecord record) {
+        if (record == null) {
+            return false;
+        }
+        boolean added = mServices.add(record);
+        if (added && record.serviceInfo != null) {
+            mApp.getWindowProcessController().onServiceStarted(record.serviceInfo);
+        }
+        return added;
+    }
+
+    /**
+     * Records a service as stopped. Note that like {@link #startService(ServiceRecord)} this method
+     * does not actually stop the service, but records the service as stopped for bookkeeping.
+     *
+     * @return true if the service was removed, false otherwise.
+     */
+    boolean stopService(ServiceRecord record) {
+        return mServices.remove(record);
+    }
+
+    /**
+     * The same as calling {@link #stopService(ServiceRecord)} on all current running services.
+     */
+    void stopAllServices() {
+        mServices.clear();
+    }
+
+    /**
+     * Returns the number of services added with {@link #startService(ServiceRecord)} and not yet
+     * removed by a call to {@link #stopService(ServiceRecord)} or {@link #stopAllServices()}.
+     *
+     * @see #startService(ServiceRecord)
+     * @see #stopService(ServiceRecord)
+     */
+    int numberOfRunningServices() {
+        return mServices.size();
+    }
+
+    /**
+     * Returns the service at the specified {@code index}.
+     *
+     * @see #numberOfRunningServices()
+     */
+    ServiceRecord getRunningServiceAt(int index) {
+        return mServices.valueAt(index);
+    }
+
+    void startExecutingService(ServiceRecord service) {
+        mExecutingServices.add(service);
+    }
+
+    void stopExecutingService(ServiceRecord service) {
+        mExecutingServices.remove(service);
+    }
+
+    void stopAllExecutingServices() {
+        mExecutingServices.clear();
+    }
+
+    ServiceRecord getExecutingServiceAt(int index) {
+        return mExecutingServices.valueAt(index);
+    }
+
+    int numberOfExecutingServices() {
+        return mExecutingServices.size();
+    }
+
+    void addConnection(ConnectionRecord connection) {
+        mConnections.add(connection);
+    }
+
+    void removeConnection(ConnectionRecord connection) {
+        mConnections.remove(connection);
+    }
+
+    void removeAllConnections() {
+        mConnections.clear();
+    }
+
+    ConnectionRecord getConnectionAt(int index) {
+        return mConnections.valueAt(index);
+    }
+
+    int numberOfConnections() {
+        return mConnections.size();
+    }
+
+    void addBoundClientUid(int clientUid) {
+        mBoundClientUids.add(clientUid);
+        mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
+    }
+
+    void updateBoundClientUids() {
+        if (mServices.isEmpty()) {
+            clearBoundClientUids();
+            return;
+        }
+        // grab a set of clientUids of all mConnections of all services
+        final ArraySet<Integer> boundClientUids = new ArraySet<>();
+        final int serviceCount = mServices.size();
+        for (int j = 0; j < serviceCount; j++) {
+            final ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns =
+                    mServices.valueAt(j).getConnections();
+            final int size = conns.size();
+            for (int conni = 0; conni < size; conni++) {
+                ArrayList<ConnectionRecord> c = conns.valueAt(conni);
+                for (int i = 0; i < c.size(); i++) {
+                    boundClientUids.add(c.get(i).clientUid);
+                }
+            }
+        }
+        mBoundClientUids = boundClientUids;
+        mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
+    }
+
+    void addBoundClientUidsOfNewService(ServiceRecord sr) {
+        if (sr == null) {
+            return;
+        }
+        ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns = sr.getConnections();
+        for (int conni = conns.size() - 1; conni >= 0; conni--) {
+            ArrayList<ConnectionRecord> c = conns.valueAt(conni);
+            for (int i = 0; i < c.size(); i++) {
+                mBoundClientUids.add(c.get(i).clientUid);
+            }
+        }
+        mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
+    }
+
+    void clearBoundClientUids() {
+        mBoundClientUids.clear();
+        mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
+    }
+
+    @GuardedBy("mService")
+    boolean incServiceCrashCountLocked(long now) {
+        final boolean procIsBoundForeground = mApp.mState.getCurProcState()
+                == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+        boolean tryAgain = false;
+        // Bump up the crash count of any services currently running in the proc.
+        for (int i = numberOfRunningServices() - 1; i >= 0; i--) {
+            // Any services running in the application need to be placed
+            // back in the pending list.
+            ServiceRecord sr = getRunningServiceAt(i);
+            // If the service was restarted a while ago, then reset crash count, else increment it.
+            if (now > sr.restartTime + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
+                sr.crashCount = 1;
+            } else {
+                sr.crashCount++;
+            }
+            // Allow restarting for started or bound foreground services that are crashing.
+            // This includes wallpapers.
+            if (sr.crashCount < mService.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY
+                    && (sr.isForeground || procIsBoundForeground)) {
+                tryAgain = true;
+            }
+        }
+        return tryAgain;
+    }
+
+    @GuardedBy("mService")
+    void onCleanupApplicationRecordLocked() {
+        mTreatLikeActivity = false;
+        mHasAboveClient = false;
+        setHasClientActivities(false);
+    }
+
+    void dump(PrintWriter pw, String prefix, long nowUptime) {
+        if (mHasForegroundServices || mApp.mState.getForcingToImportant() != null) {
+            pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices);
+            pw.print(" forcingToImportant="); pw.println(mApp.mState.getForcingToImportant());
+        }
+        if (mHasClientActivities || mHasAboveClient || mTreatLikeActivity) {
+            pw.print(prefix); pw.print("hasClientActivities="); pw.print(mHasClientActivities);
+            pw.print(" hasAboveClient="); pw.print(mHasAboveClient);
+            pw.print(" treatLikeActivity="); pw.println(mTreatLikeActivity);
+        }
+        if (mConnectionService != null || mConnectionGroup != 0) {
+            pw.print(prefix); pw.print("connectionGroup="); pw.print(mConnectionGroup);
+            pw.print(" Importance="); pw.print(mConnectionImportance);
+            pw.print(" Service="); pw.println(mConnectionService);
+        }
+        if (mAllowlistManager) {
+            pw.print(prefix); pw.print("allowlistManager="); pw.println(mAllowlistManager);
+        }
+        if (mServices.size() > 0) {
+            pw.print(prefix); pw.println("Services:");
+            for (int i = 0, size = mServices.size(); i < size; i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(mServices.valueAt(i));
+            }
+        }
+        if (mExecutingServices.size() > 0) {
+            pw.print(prefix); pw.print("Executing Services (fg=");
+            pw.print(mExecServicesFg); pw.println(")");
+            for (int i = 0, size = mExecutingServices.size(); i < size; i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(mExecutingServices.valueAt(i));
+            }
+        }
+        if (mConnections.size() > 0) {
+            pw.print(prefix); pw.println("mConnections:");
+            for (int i = 0, size = mConnections.size(); i < size; i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(mConnections.valueAt(i));
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
new file mode 100644
index 0000000..e1a153d
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -0,0 +1,1337 @@
+/*
+ * Copyright (C) 2020 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.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
+import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
+import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.NFC_UID;
+import static android.os.Process.ROOT_UID;
+import static android.os.Process.SHELL_UID;
+import static android.os.Process.SYSTEM_UID;
+
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_COMPANION_APP;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_UID;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED;
+import static com.android.server.am.ActiveServices.fgsCodeToString;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.ProcessRecord.TAG;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.os.Binder;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.CompositeRWLock;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FrameworkStatsLog;
+
+
+import java.io.PrintWriter;
+
+/**
+ * The state info of the process, including proc state, oom adj score, et al.
+ */
+final class ProcessStateRecord {
+    private final ProcessRecord mApp;
+    private final ActivityManagerService mService;
+    private final ActivityManagerGlobalLock mProcLock;
+
+    /**
+     * Maximum OOM adjustment for this process.
+     */
+    @GuardedBy("mService")
+    private int mMaxAdj = ProcessList.UNKNOWN_ADJ;
+
+    /**
+     *  Current OOM unlimited adjustment for this process.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mCurRawAdj = ProcessList.INVALID_ADJ;
+
+    /**
+     * Last set OOM unlimited adjustment for this process.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mSetRawAdj = ProcessList.INVALID_ADJ;
+
+    /**
+     * Current OOM adjustment for this process.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mCurAdj = ProcessList.INVALID_ADJ;
+
+    /**
+     * Last set OOM adjustment for this process.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mSetAdj = ProcessList.INVALID_ADJ;
+
+    /**
+     * The last adjustment that was verified as actually being set.
+     */
+    @GuardedBy("mService")
+    private int mVerifiedAdj = ProcessList.INVALID_ADJ;
+
+    /**
+     * Current capability flags of this process.
+     * For example, PROCESS_CAPABILITY_FOREGROUND_LOCATION is one capability.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mCurCapability = PROCESS_CAPABILITY_NONE;
+
+    /**
+     * Last set capability flags.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mSetCapability = PROCESS_CAPABILITY_NONE;
+
+    /**
+     * Currently desired scheduling class.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mCurSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+
+    /**
+     * Last set to background scheduling class.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mSetSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+
+    /**
+     * Currently computed process state.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mCurProcState = PROCESS_STATE_NONEXISTENT;
+
+    /**
+     * Last reported process state.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mRepProcState = PROCESS_STATE_NONEXISTENT;
+
+    /**
+     * Temp state during computation.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mCurRawProcState = PROCESS_STATE_NONEXISTENT;
+
+    /**
+     * Last set process state in process tracker.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mSetProcState = PROCESS_STATE_NONEXISTENT;
+
+    /**
+     * Last time mSetProcState changed.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private long mLastStateTime;
+
+    /**
+     * Previous priority value if we're switching to non-SCHED_OTHER.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mSavedPriority;
+
+    /**
+     * Process currently is on the service B list.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mServiceB;
+
+    /**
+     * We are forcing to service B list due to its RAM use.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mServiceHighRam;
+
+    /**
+     * Has this process not been in a cached state since last idle?
+     */
+    @GuardedBy("mProcLock")
+    private boolean mNotCachedSinceIdle;
+
+    /**
+     * Are there any started services running in this process?
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mHasStartedServices;
+
+    /**
+     * Running any activities that are foreground?
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mHasForegroundActivities;
+
+    /**
+     * Last reported foreground activities.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mRepForegroundActivities;
+
+    /**
+     * Has UI been shown in this process since it was started?
+     */
+    @GuardedBy("mService")
+    private boolean mHasShownUi;
+
+    /**
+     * Is this process currently showing a non-activity UI that the user
+     * is interacting with? E.g. The status bar when it is expanded, but
+     * not when it is minimized. When true the
+     * process will be set to use the ProcessList#SCHED_GROUP_TOP_APP
+     * scheduling group to boost performance.
+     */
+    @GuardedBy("mService")
+    private boolean mHasTopUi;
+
+    /**
+     * Is the process currently showing a non-activity UI that
+     * overlays on-top of activity UIs on screen. E.g. display a window
+     * of type android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY
+     * When true the process will oom adj score will be set to
+     * ProcessList#PERCEPTIBLE_APP_ADJ at minimum to reduce the chance
+     * of the process getting killed.
+     */
+    @GuardedBy("mService")
+    private boolean mHasOverlayUi;
+
+    /**
+     * Is the process currently running a RemoteAnimation? When true
+     * the process will be set to use the
+     * ProcessList#SCHED_GROUP_TOP_APP scheduling group to boost
+     * performance, as well as oom adj score will be set to
+     * ProcessList#VISIBLE_APP_ADJ at minimum to reduce the chance
+     * of the process getting killed.
+     */
+    @GuardedBy("mService")
+    private boolean mRunningRemoteAnimation;
+
+    /**
+     * Keep track of whether we changed 'mSetAdj'.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mProcStateChanged;
+
+    /**
+     * Whether we have told usage stats about it being an interaction.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mReportedInteraction;
+
+    /**
+     * The time we sent the last interaction event.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private long mInteractionEventTime;
+
+    /**
+     * When we became foreground for interaction purposes.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private long mFgInteractionTime;
+
+    /**
+     * Token that is forcing this process to be important.
+     */
+    @GuardedBy("mService")
+    private Object mForcingToImportant;
+
+    /**
+     * Sequence id for identifying oom_adj assignment cycles.
+     */
+    @GuardedBy("mService")
+    private int mAdjSeq;
+
+    /**
+     * Sequence id for identifying oom_adj assignment cycles.
+     */
+    @GuardedBy("mService")
+    private int mCompletedAdjSeq;
+
+    /**
+     * Whether this app has encountered a cycle in the most recent update.
+     */
+    @GuardedBy("mService")
+    private boolean mContainsCycle;
+
+    /**
+     * When (uptime) the process last became unimportant.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private long mWhenUnimportant;
+
+    /**
+     * The last time the process was in the TOP state or greater.
+     */
+    @GuardedBy("mService")
+    private long mLastTopTime;
+
+    /**
+     * Is this an empty background process?
+     */
+    @GuardedBy("mService")
+    private boolean mEmpty;
+
+    /**
+     * Is this a cached process?
+     */
+    @GuardedBy("mService")
+    private boolean mCached;
+
+    /**
+     * This is a system process, but not currently showing UI.
+     */
+    @GuardedBy("mService")
+    private boolean mSystemNoUi;
+
+    /**
+     * If the proc state is PROCESS_STATE_BOUND_FOREGROUND_SERVICE or above, it can start FGS.
+     * It must obtain the proc state from a persistent/top process or FGS, not transitive.
+     */
+    @GuardedBy("mService")
+    private int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
+
+    @GuardedBy("mService")
+    private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>();
+
+    /**
+     * Does the process has permission to start FGS from background.
+     */
+    @GuardedBy("mService")
+    private @ActiveServices.FgsFeatureRetCode int mAllowStartFgsByPermission;
+
+    /**
+     * Can this process start FGS from background?
+     * If this process has the ability to start FGS from background, this ability can be passed to
+     * another process through service binding.
+     */
+    @GuardedBy("mService")
+    private @ActiveServices.FgsFeatureRetCode int mAllowStartFgs;
+
+    /**
+     * Debugging: primary thing impacting oom_adj.
+     */
+    @GuardedBy("mService")
+    private String mAdjType;
+
+    /**
+     * Debugging: adj code to report to app.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mAdjTypeCode;
+
+    /**
+     * Debugging: option dependent object.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private Object mAdjSource;
+
+    /**
+     * Debugging: proc state of mAdjSource's process.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mAdjSourceProcState;
+
+    /**
+     * Debugging: target component impacting oom_adj.
+     */
+    @CompositeRWLock({"mService", "mProcLock"})
+    private Object mAdjTarget;
+
+    /**
+     * Approximates the usage count of the app, used for cache re-ranking by CacheOomRanker.
+     *
+     * Counts the number of times the process is re-added to the cache (i.e. setCached(false);
+     * setCached(true)). This over counts, as setCached is sometimes reset while remaining in the
+     * cache. However, this happens uniformly across processes, so ranking is not affected.
+     */
+    @GuardedBy("mService")
+    private int mCacheOomRankerUseCount;
+
+    /**
+     * Whether or not this process is reachable from given process.
+     */
+    @GuardedBy("mService")
+    private boolean mReachable;
+
+    // Below are the cached task info for OomAdjuster only
+    private static final int VALUE_INVALID = -1;
+    private static final int VALUE_FALSE = 0;
+    private static final int VALUE_TRUE = 1;
+
+    @GuardedBy("mService")
+    private int mCachedHasActivities = VALUE_INVALID;
+    @GuardedBy("mService")
+    private int mCachedIsHeavyWeight = VALUE_INVALID;
+    @GuardedBy("mService")
+    private int mCachedHasVisibleActivities = VALUE_INVALID;
+    @GuardedBy("mService")
+    private int mCachedIsHomeProcess = VALUE_INVALID;
+    @GuardedBy("mService")
+    private int mCachedIsPreviousProcess = VALUE_INVALID;
+    @GuardedBy("mService")
+    private int mCachedHasRecentTasks = VALUE_INVALID;
+    @GuardedBy("mService")
+    private int mCachedIsReceivingBroadcast = VALUE_INVALID;
+
+    @GuardedBy("mService")
+    private int mCachedAdj = ProcessList.INVALID_ADJ;
+    @GuardedBy("mService")
+    private boolean mCachedForegroundActivities = false;
+    @GuardedBy("mService")
+    private int mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+    @GuardedBy("mService")
+    private int mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+
+    ProcessStateRecord(ProcessRecord app) {
+        mApp = app;
+        mService = app.mService;
+        mProcLock = mService.mProcLock;
+        setAllowStartFgsByPermission();
+    }
+
+    void init(long now) {
+        mLastStateTime = now;
+    }
+
+    @GuardedBy("mService")
+    void setMaxAdj(int maxAdj) {
+        mMaxAdj = maxAdj;
+    }
+
+    @GuardedBy("mService")
+    int getMaxAdj() {
+        return mMaxAdj;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setCurRawAdj(int curRawAdj) {
+        mCurRawAdj = curRawAdj;
+        mApp.getWindowProcessController().setPerceptible(
+                curRawAdj <= ProcessList.PERCEPTIBLE_APP_ADJ);
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getCurRawAdj() {
+        return mCurRawAdj;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setSetRawAdj(int setRawAdj) {
+        mSetRawAdj = setRawAdj;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getSetRawAdj() {
+        return mSetRawAdj;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setCurAdj(int curAdj) {
+        mCurAdj = curAdj;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getCurAdj() {
+        return mCurAdj;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setSetAdj(int setAdj) {
+        mSetAdj = setAdj;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getSetAdj() {
+        return mSetAdj;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getSetAdjWithServices() {
+        if (mSetAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+            if (mHasStartedServices) {
+                return ProcessList.SERVICE_B_ADJ;
+            }
+        }
+        return mSetAdj;
+    }
+
+    @GuardedBy("mService")
+    void setVerifiedAdj(int verifiedAdj) {
+        mVerifiedAdj = verifiedAdj;
+    }
+
+    @GuardedBy("mService")
+    int getVerifiedAdj() {
+        return mVerifiedAdj;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setCurCapability(int curCapability) {
+        mCurCapability = curCapability;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getCurCapability() {
+        return mCurCapability;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setSetCapability(int setCapability) {
+        mSetCapability = setCapability;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getSetCapability() {
+        return mSetCapability;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setCurrentSchedulingGroup(int curSchedGroup) {
+        mCurSchedGroup = curSchedGroup;
+        mApp.getWindowProcessController().setCurrentSchedulingGroup(curSchedGroup);
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getCurrentSchedulingGroup() {
+        return mCurSchedGroup;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setSetSchedGroup(int setSchedGroup) {
+        mSetSchedGroup = setSchedGroup;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getSetSchedGroup() {
+        return mSetSchedGroup;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setCurProcState(int curProcState) {
+        mCurProcState = curProcState;
+        mApp.getWindowProcessController().setCurrentProcState(mCurProcState);
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getCurProcState() {
+        return mCurProcState;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setCurRawProcState(int curRawProcState) {
+        mCurRawProcState = curRawProcState;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getCurRawProcState() {
+        return mCurRawProcState;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setReportedProcState(int repProcState) {
+        mRepProcState = repProcState;
+        mApp.getPkgList().forEachPackage((pkgName, holder) ->
+                FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+                    mApp.uid, mApp.processName, pkgName,
+                    ActivityManager.processStateAmToProto(mRepProcState),
+                    holder.appVersion)
+        );
+        mApp.getWindowProcessController().setReportedProcState(repProcState);
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getReportedProcState() {
+        return mRepProcState;
+    }
+
+    @GuardedBy("mService")
+    void forceProcessStateUpTo(int newState) {
+        if (mRepProcState > newState) {
+            synchronized (mProcLock) {
+                mRepProcState = newState;
+                setCurProcState(newState);
+                setCurRawProcState(newState);
+                mApp.getPkgList().forEachPackage((pkgName, holder) ->
+                        FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+                            mApp.uid, mApp.processName, pkgName,
+                            ActivityManager.processStateAmToProto(mRepProcState),
+                            holder.appVersion)
+                );
+            }
+        }
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setSetProcState(int setProcState) {
+        mSetProcState = setProcState;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getSetProcState() {
+        return mSetProcState;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setLastStateTime(long lastStateTime) {
+        mLastStateTime = lastStateTime;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    long getLastStateTime() {
+        return mLastStateTime;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setSavedPriority(int savedPriority) {
+        mSavedPriority = savedPriority;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getSavedPriority() {
+        return mSavedPriority;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setServiceB(boolean serviceb) {
+        mServiceB = serviceb;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isServiceB() {
+        return mServiceB;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setServiceHighRam(boolean serviceHighRam) {
+        mServiceHighRam = serviceHighRam;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isServiceHighRam() {
+        return mServiceHighRam;
+    }
+
+    @GuardedBy("mProcLock")
+    void setNotCachedSinceIdle(boolean notCachedSinceIdle) {
+        mNotCachedSinceIdle = notCachedSinceIdle;
+    }
+
+    @GuardedBy("mProcLock")
+    boolean isNotCachedSinceIdle() {
+        return mNotCachedSinceIdle;
+    }
+
+    @GuardedBy("mProcLock")
+    void setHasStartedServices(boolean hasStartedServices) {
+        mHasStartedServices = hasStartedServices;
+    }
+
+    @GuardedBy("mProcLock")
+    boolean hasStartedServices() {
+        return mHasStartedServices;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setHasForegroundActivities(boolean hasForegroundActivities) {
+        mHasForegroundActivities = hasForegroundActivities;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean hasForegroundActivities() {
+        return mHasForegroundActivities;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setRepForegroundActivities(boolean repForegroundActivities) {
+        mRepForegroundActivities = repForegroundActivities;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean hasRepForegroundActivities() {
+        return mRepForegroundActivities;
+    }
+
+    @GuardedBy("mService")
+    void setHasShownUi(boolean hasShownUi) {
+        mHasShownUi = hasShownUi;
+    }
+
+    @GuardedBy("mService")
+    boolean hasShownUi() {
+        return mHasShownUi;
+    }
+
+    @GuardedBy("mService")
+    void setHasTopUi(boolean hasTopUi) {
+        mHasTopUi = hasTopUi;
+        mApp.getWindowProcessController().setHasTopUi(hasTopUi);
+    }
+
+    @GuardedBy("mService")
+    boolean hasTopUi() {
+        return mHasTopUi;
+    }
+
+    @GuardedBy("mService")
+    void setHasOverlayUi(boolean hasOverlayUi) {
+        mHasOverlayUi = hasOverlayUi;
+        mApp.getWindowProcessController().setHasOverlayUi(hasOverlayUi);
+    }
+
+    @GuardedBy("mService")
+    boolean hasOverlayUi() {
+        return mHasOverlayUi;
+    }
+
+    @GuardedBy("mService")
+    boolean isRunningRemoteAnimation() {
+        return mRunningRemoteAnimation;
+    }
+
+    @GuardedBy("mService")
+    void setRunningRemoteAnimation(boolean runningRemoteAnimation) {
+        if (mRunningRemoteAnimation == runningRemoteAnimation) {
+            return;
+        }
+        mRunningRemoteAnimation = runningRemoteAnimation;
+        if (DEBUG_OOM_ADJ) {
+            Slog.i(TAG, "Setting runningRemoteAnimation=" + runningRemoteAnimation
+                    + " for pid=" + mApp.getPid());
+        }
+        mService.updateOomAdjLocked(mApp, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setProcStateChanged(boolean procStateChanged) {
+        mProcStateChanged = procStateChanged;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean hasProcStateChanged() {
+        return mProcStateChanged;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setReportedInteraction(boolean reportedInteraction) {
+        mReportedInteraction = reportedInteraction;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean hasReportedInteraction() {
+        return mReportedInteraction;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setInteractionEventTime(long interactionEventTime) {
+        mInteractionEventTime = interactionEventTime;
+        mApp.getWindowProcessController().setInteractionEventTime(interactionEventTime);
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    long getInteractionEventTime() {
+        return mInteractionEventTime;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setFgInteractionTime(long fgInteractionTime) {
+        mFgInteractionTime = fgInteractionTime;
+        mApp.getWindowProcessController().setFgInteractionTime(fgInteractionTime);
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    long getFgInteractionTime() {
+        return mFgInteractionTime;
+    }
+
+    @GuardedBy("mService")
+    void setForcingToImportant(Object forcingToImportant) {
+        mForcingToImportant = forcingToImportant;
+    }
+
+    @GuardedBy("mService")
+    Object getForcingToImportant() {
+        return mForcingToImportant;
+    }
+
+    @GuardedBy("mService")
+    void setAdjSeq(int adjSeq) {
+        mAdjSeq = adjSeq;
+    }
+
+    @GuardedBy("mService")
+    void decAdjSeq() {
+        mAdjSeq--;
+    }
+
+    @GuardedBy("mService")
+    int getAdjSeq() {
+        return mAdjSeq;
+    }
+
+    @GuardedBy("mService")
+    void setCompletedAdjSeq(int completedAdjSeq) {
+        mCompletedAdjSeq = completedAdjSeq;
+    }
+
+    @GuardedBy("mService")
+    void decCompletedAdjSeq() {
+        mCompletedAdjSeq--;
+    }
+
+    @GuardedBy("mService")
+    int getCompletedAdjSeq() {
+        return mCompletedAdjSeq;
+    }
+
+    @GuardedBy("mService")
+    void setContainsCycle(boolean containsCycle) {
+        mContainsCycle = containsCycle;
+    }
+
+    @GuardedBy("mService")
+    boolean containsCycle() {
+        return mContainsCycle;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setWhenUnimportant(long whenUnimportant) {
+        mWhenUnimportant = whenUnimportant;
+        mApp.getWindowProcessController().setWhenUnimportant(whenUnimportant);
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    long getWhenUnimportant() {
+        return mWhenUnimportant;
+    }
+
+    @GuardedBy("mService")
+    void setLastTopTime(long lastTopTime) {
+        mLastTopTime = lastTopTime;
+    }
+
+    @GuardedBy("mService")
+    long getLastTopTime() {
+        return mLastTopTime;
+    }
+
+    @GuardedBy("mService")
+    void setEmpty(boolean empty) {
+        mEmpty = empty;
+    }
+
+    @GuardedBy("mService")
+    boolean isEmpty() {
+        return mEmpty;
+    }
+
+    @GuardedBy("mService")
+    void setCached(boolean cached) {
+        if (mCached != cached) {
+            mCached = cached;
+            if (cached) {
+                ++mCacheOomRankerUseCount;
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    boolean isCached() {
+        return mCached;
+    }
+
+    @GuardedBy("mService")
+    int getCacheOomRankerUseCount() {
+        return mCacheOomRankerUseCount;
+    }
+
+    @GuardedBy("mService")
+    void setSystemNoUi(boolean systemNoUi) {
+        mSystemNoUi = systemNoUi;
+    }
+
+    @GuardedBy("mService")
+    boolean isSystemNoUi() {
+        return mSystemNoUi;
+    }
+
+    @GuardedBy("mService")
+    void setAdjType(String adjType) {
+        mAdjType = adjType;
+    }
+
+    @GuardedBy("mService")
+    String getAdjType() {
+        return mAdjType;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setAdjTypeCode(int adjTypeCode) {
+        mAdjTypeCode = adjTypeCode;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getAdjTypeCode() {
+        return mAdjTypeCode;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setAdjSource(Object adjSource) {
+        mAdjSource = adjSource;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    Object getAdjSource() {
+        return mAdjSource;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setAdjSourceProcState(int adjSourceProcState) {
+        mAdjSourceProcState = adjSourceProcState;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getAdjSourceProcState() {
+        return mAdjSourceProcState;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setAdjTarget(Object adjTarget) {
+        mAdjTarget = adjTarget;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    Object getAdjTarget() {
+        return mAdjTarget;
+    }
+
+    @GuardedBy("mService")
+    boolean isReachable() {
+        return mReachable;
+    }
+
+    @GuardedBy("mService")
+    void setReachable(boolean reachable) {
+        mReachable = reachable;
+    }
+
+    @GuardedBy("mService")
+    void resetCachedInfo() {
+        mCachedHasActivities = VALUE_INVALID;
+        mCachedIsHeavyWeight = VALUE_INVALID;
+        mCachedHasVisibleActivities = VALUE_INVALID;
+        mCachedIsHomeProcess = VALUE_INVALID;
+        mCachedIsPreviousProcess = VALUE_INVALID;
+        mCachedHasRecentTasks = VALUE_INVALID;
+        mCachedIsReceivingBroadcast = VALUE_INVALID;
+        mCachedAdj = ProcessList.INVALID_ADJ;
+        mCachedForegroundActivities = false;
+        mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+        mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+    }
+
+    @GuardedBy("mService")
+    boolean getCachedHasActivities() {
+        if (mCachedHasActivities == VALUE_INVALID) {
+            mCachedHasActivities = mApp.getWindowProcessController().hasActivities() ? VALUE_TRUE
+                    : VALUE_FALSE;
+        }
+        return mCachedHasActivities == VALUE_TRUE;
+    }
+
+    @GuardedBy("mService")
+    boolean getCachedIsHeavyWeight() {
+        if (mCachedIsHeavyWeight == VALUE_INVALID) {
+            mCachedIsHeavyWeight = mApp.getWindowProcessController().isHeavyWeightProcess()
+                    ? VALUE_TRUE : VALUE_FALSE;
+        }
+        return mCachedIsHeavyWeight == VALUE_TRUE;
+    }
+
+    @GuardedBy("mService")
+    boolean getCachedHasVisibleActivities() {
+        if (mCachedHasVisibleActivities == VALUE_INVALID) {
+            mCachedHasVisibleActivities = mApp.getWindowProcessController().hasVisibleActivities()
+                    ? VALUE_TRUE : VALUE_FALSE;
+        }
+        return mCachedHasVisibleActivities == VALUE_TRUE;
+    }
+
+    @GuardedBy("mService")
+    boolean getCachedIsHomeProcess() {
+        if (mCachedIsHomeProcess == VALUE_INVALID) {
+            if (mApp.getWindowProcessController().isHomeProcess()) {
+                mCachedIsHomeProcess = VALUE_TRUE;
+                mService.mAppProfiler.mHasHomeProcess = true;
+            } else {
+                mCachedIsHomeProcess = VALUE_FALSE;
+            }
+        }
+        return mCachedIsHomeProcess == VALUE_TRUE;
+    }
+
+    @GuardedBy("mService")
+    boolean getCachedIsPreviousProcess() {
+        if (mCachedIsPreviousProcess == VALUE_INVALID) {
+            if (mApp.getWindowProcessController().isPreviousProcess()) {
+                mCachedIsPreviousProcess = VALUE_TRUE;
+                mService.mAppProfiler.mHasPreviousProcess = true;
+            } else {
+                mCachedIsPreviousProcess = VALUE_FALSE;
+            }
+        }
+        return mCachedIsPreviousProcess == VALUE_TRUE;
+    }
+
+    @GuardedBy("mService")
+    boolean getCachedHasRecentTasks() {
+        if (mCachedHasRecentTasks == VALUE_INVALID) {
+            mCachedHasRecentTasks = mApp.getWindowProcessController().hasRecentTasks()
+                    ? VALUE_TRUE : VALUE_FALSE;
+        }
+        return mCachedHasRecentTasks == VALUE_TRUE;
+    }
+
+    @GuardedBy("mService")
+    boolean getCachedIsReceivingBroadcast(ArraySet<BroadcastQueue> tmpQueue) {
+        if (mCachedIsReceivingBroadcast == VALUE_INVALID) {
+            tmpQueue.clear();
+            mCachedIsReceivingBroadcast = mService.isReceivingBroadcastLocked(mApp, tmpQueue)
+                    ? VALUE_TRUE : VALUE_FALSE;
+            if (mCachedIsReceivingBroadcast == VALUE_TRUE) {
+                mCachedSchedGroup = tmpQueue.contains(mService.mFgBroadcastQueue)
+                        ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
+            }
+        }
+        return mCachedIsReceivingBroadcast == VALUE_TRUE;
+    }
+
+    @GuardedBy("mService")
+    void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback,
+            int adj, boolean foregroundActivities, int procState, int schedGroup, int appUid,
+            int logUid, int processCurTop) {
+        if (mCachedAdj != ProcessList.INVALID_ADJ) {
+            return;
+        }
+        callback.initialize(mApp, adj, foregroundActivities, procState, schedGroup, appUid, logUid,
+                processCurTop);
+        final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX,
+                mApp.getWindowProcessController().computeOomAdjFromActivities(callback));
+
+        mCachedAdj = callback.adj;
+        mCachedForegroundActivities = callback.foregroundActivities;
+        mCachedProcState = callback.procState;
+        mCachedSchedGroup = callback.schedGroup;
+
+        if (mCachedAdj == ProcessList.VISIBLE_APP_ADJ) {
+            mCachedAdj += minLayer;
+        }
+    }
+
+    @GuardedBy("mService")
+    int getCachedAdj() {
+        return mCachedAdj;
+    }
+
+    @GuardedBy("mService")
+    boolean getCachedForegroundActivities() {
+        return mCachedForegroundActivities;
+    }
+
+    @GuardedBy("mService")
+    int getCachedProcState() {
+        return mCachedProcState;
+    }
+
+    @GuardedBy("mService")
+    int getCachedSchedGroup() {
+        return mCachedSchedGroup;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    public String makeAdjReason() {
+        if (mAdjSource != null || mAdjTarget != null) {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append(' ');
+            if (mAdjTarget instanceof ComponentName) {
+                sb.append(((ComponentName) mAdjTarget).flattenToShortString());
+            } else if (mAdjTarget != null) {
+                sb.append(mAdjTarget.toString());
+            } else {
+                sb.append("{null}");
+            }
+            sb.append("<=");
+            if (mAdjSource instanceof ProcessRecord) {
+                sb.append("Proc{");
+                sb.append(((ProcessRecord) mAdjSource).toShortString());
+                sb.append("}");
+            } else if (mAdjSource != null) {
+                sb.append(mAdjSource.toString());
+            } else {
+                sb.append("{null}");
+            }
+            return sb.toString();
+        }
+        return null;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void onCleanupApplicationRecordLSP() {
+        setHasForegroundActivities(false);
+        mHasShownUi = false;
+        mForcingToImportant = null;
+        mCurRawAdj = mSetRawAdj = mCurAdj = mSetAdj = mVerifiedAdj = ProcessList.INVALID_ADJ;
+        mCurCapability = mSetCapability = PROCESS_CAPABILITY_NONE;
+        mCurSchedGroup = mSetSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+        mCurProcState = mRepProcState = mCurRawProcState = mSetProcState = mAllowStartFgsState =
+                PROCESS_STATE_NONEXISTENT;
+    }
+
+    @GuardedBy("mService")
+    void addAllowBackgroundFgsStartsToken(Binder entity) {
+        mBackgroundFgsStartTokens.add(entity);
+    }
+
+    @GuardedBy("mService")
+    void removeAllowBackgroundFgsStartsToken(Binder entity) {
+        mBackgroundFgsStartTokens.remove(entity);
+    }
+
+    @GuardedBy("mService")
+    boolean areBackgroundFgsStartsAllowedByToken() {
+        return !mBackgroundFgsStartTokens.isEmpty();
+    }
+
+    @GuardedBy("mService")
+    void resetAllowStartFgs() {
+        mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
+        mAllowStartFgs = mAllowStartFgsByPermission;
+    }
+
+    @GuardedBy("mService")
+    void bumpAllowStartFgsState(int newProcState) {
+        if (newProcState < mAllowStartFgsState) {
+            mAllowStartFgsState = newProcState;
+        }
+    }
+
+    @GuardedBy("mService")
+    void setAllowStartFgsState(int allowStartFgsState) {
+        mAllowStartFgsState = allowStartFgsState;
+    }
+
+    @GuardedBy("mService")
+    boolean isAllowedStartFgsState() {
+        return mAllowStartFgsState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+    }
+
+    @GuardedBy("mService")
+    void setAllowStartFgsByPermission() {
+        int ret = FGS_FEATURE_DENIED;
+        if (ret == FGS_FEATURE_DENIED) {
+            boolean isSystem = false;
+            final int uid = UserHandle.getAppId(mApp.info.uid);
+            switch (uid) {
+                case ROOT_UID:
+                case SYSTEM_UID:
+                case NFC_UID:
+                case SHELL_UID:
+                    isSystem = true;
+                    break;
+                default:
+                    isSystem = false;
+                    break;
+            }
+
+            if (isSystem) {
+                ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_UID;
+            }
+        }
+
+        if (ret == FGS_FEATURE_DENIED) {
+            if (ActivityManager.checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND,
+                    mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
+                ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION;
+            } else if (ActivityManager.checkComponentPermission(
+                    START_FOREGROUND_SERVICES_FROM_BACKGROUND,
+                    mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
+                ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION;
+            } else if (ActivityManager.checkComponentPermission(SYSTEM_ALERT_WINDOW,
+                    mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
+                ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION;
+            }
+        }
+        mAllowStartFgs = mAllowStartFgsByPermission = ret;
+    }
+
+    @GuardedBy("mService")
+    void setAllowStartFgs() {
+        if (mAllowStartFgs != FGS_FEATURE_DENIED) {
+            return;
+        }
+        if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+            if (isAllowedStartFgsState()) {
+                mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+            }
+        }
+
+        if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+            // Is the calling UID a device owner app?
+            if (mService.mInternal != null) {
+                if (mService.mInternal.isDeviceOwner(mApp.info.uid)) {
+                    mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER;
+                }
+            }
+        }
+
+        if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+            if (mService.mInternal != null) {
+                final boolean isCompanionApp = mService.mInternal.isAssociatedCompanionApp(
+                        UserHandle.getUserId(mApp.info.uid), mApp.info.uid);
+                if (isCompanionApp) {
+                    mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_COMPANION_APP;
+                }
+            }
+        }
+
+        if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+            // Is the calling UID a profile owner app?
+            if (mService.mInternal != null) {
+                if (mService.mInternal.isProfileOwner(mApp.info.uid)) {
+                    mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER;
+                }
+            }
+        }
+
+        if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+            // uid is on DeviceIdleController's user/system allowlist
+            // or AMS's FgsStartTempAllowList.
+            if (mService.isAllowlistedForFgsStartLOSP(mApp.info.uid)) {
+                mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void setAllowStartFgs(@ActiveServices.FgsFeatureRetCode int allowStartFgs) {
+        mAllowStartFgs = allowStartFgs;
+    }
+
+    @GuardedBy("mService")
+    @ActiveServices.FgsFeatureRetCode int getAllowedStartFgs() {
+        return mAllowStartFgs;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void dump(PrintWriter pw, String prefix, long nowUptime) {
+        if (mReportedInteraction || mFgInteractionTime != 0) {
+            pw.print(prefix); pw.print("reportedInteraction=");
+            pw.print(mReportedInteraction);
+            if (mInteractionEventTime != 0) {
+                pw.print(" time=");
+                TimeUtils.formatDuration(mInteractionEventTime, SystemClock.elapsedRealtime(), pw);
+            }
+            if (mFgInteractionTime != 0) {
+                pw.print(" fgInteractionTime=");
+                TimeUtils.formatDuration(mFgInteractionTime, SystemClock.elapsedRealtime(), pw);
+            }
+            pw.println();
+        }
+        pw.print(prefix); pw.print("adjSeq="); pw.print(mAdjSeq);
+        pw.print(" lruSeq="); pw.println(mApp.getLruSeq());
+        pw.print(prefix); pw.print("oom adj: max="); pw.print(mMaxAdj);
+        pw.print(" curRaw="); pw.print(mCurRawAdj);
+        pw.print(" setRaw="); pw.print(mSetRawAdj);
+        pw.print(" cur="); pw.print(mCurAdj);
+        pw.print(" set="); pw.println(mSetAdj);
+        pw.print(prefix); pw.print("mCurSchedGroup="); pw.print(mCurSchedGroup);
+        pw.print(" setSchedGroup="); pw.print(mSetSchedGroup);
+        pw.print(" systemNoUi="); pw.print(mSystemNoUi);
+        pw.print(prefix); pw.print("curProcState="); pw.print(getCurProcState());
+        pw.print(" mRepProcState="); pw.print(mRepProcState);
+        pw.print(" setProcState="); pw.print(mSetProcState);
+        pw.print(" lastStateTime=");
+        TimeUtils.formatDuration(getLastStateTime(), nowUptime, pw);
+        pw.println();
+        pw.print(prefix); pw.print("curCapability=");
+        ActivityManager.printCapabilitiesFull(pw, mCurCapability);
+        pw.print(" setCapability=");
+        ActivityManager.printCapabilitiesFull(pw, mSetCapability);
+        pw.println();
+        pw.print(prefix); pw.print("allowStartFgsState=");
+        pw.println(mAllowStartFgsState);
+        if (mAllowStartFgs != FGS_FEATURE_DENIED) {
+            pw.print(prefix); pw.print("allowStartFgs=");
+            pw.println(fgsCodeToString(mAllowStartFgs));
+        }
+        if (mHasShownUi || mApp.mProfile.hasPendingUiClean()) {
+            pw.print(prefix); pw.print("hasShownUi="); pw.print(mHasShownUi);
+            pw.print(" pendingUiClean="); pw.print(mApp.mProfile.hasPendingUiClean());
+        }
+        pw.print(prefix); pw.print("cached="); pw.print(mCached);
+        pw.print(" empty="); pw.println(mEmpty);
+        if (mServiceB) {
+            pw.print(prefix); pw.print("serviceb="); pw.print(mServiceB);
+            pw.print(" serviceHighRam="); pw.println(mServiceHighRam);
+        }
+        if (mNotCachedSinceIdle) {
+            pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(mNotCachedSinceIdle);
+            pw.print(" initialIdlePss="); pw.println(mApp.mProfile.getInitialIdlePss());
+        }
+        if (hasTopUi() || hasOverlayUi() || mRunningRemoteAnimation) {
+            pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
+            pw.print(" hasOverlayUi="); pw.print(hasOverlayUi());
+            pw.print(" runningRemoteAnimation="); pw.println(mRunningRemoteAnimation);
+        }
+        if (mHasForegroundActivities || mRepForegroundActivities) {
+            pw.print(prefix);
+            pw.print(" foregroundActivities="); pw.print(mHasForegroundActivities);
+            pw.print(" (rep="); pw.print(mRepForegroundActivities); pw.println(")");
+        }
+        if (mSetProcState > ActivityManager.PROCESS_STATE_SERVICE) {
+            pw.print(prefix);
+            pw.print(" whenUnimportant=");
+            TimeUtils.formatDuration(mWhenUnimportant - nowUptime, pw);
+            pw.println();
+        }
+        if (mLastTopTime > 0) {
+            pw.print(prefix); pw.print("lastTopTime=");
+            TimeUtils.formatDuration(mLastTopTime, nowUptime, pw);
+            pw.println();
+        }
+        if (mHasStartedServices) {
+            pw.print(prefix); pw.print("hasStartedServices="); pw.println(mHasStartedServices);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index c10a078..34a0c1b 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -313,8 +313,8 @@
 
     private void scheduleRequestPssAllProcs(boolean always, boolean memLowered) {
         mAm.mHandler.post(() -> {
-            synchronized (mAm) {
-                mAm.mAppProfiler.requestPssAllProcsLocked(
+            synchronized (mAm.mProcLock) {
+                mAm.mAppProfiler.requestPssAllProcsLPr(
                         SystemClock.uptimeMillis(), always, memLowered);
             }
         });
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index 29c1657..072eba5 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.ComponentName.WithComponentName;
 import android.os.Binder;
@@ -23,6 +24,7 @@
 import android.os.UserHandle;
 import android.util.Slog;
 import android.util.SparseArray;
+
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
@@ -372,10 +374,11 @@
      */
     private void dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw,
             final ContentProviderRecord r, String[] args, boolean dumpAll) {
+        final IApplicationThread thread = r.proc != null ? r.proc.getThread() : null;
         for (String s: args) {
             if (!dumpAll && s.contains("--proto")) {
-                if (r.proc != null && r.proc.thread != null) {
-                    dumpToTransferPipe(null , fd, pw, r, args);
+                if (thread != null) {
+                    dumpToTransferPipe(null , fd, pw, r, thread, args);
                 }
                 return;
             }
@@ -386,7 +389,7 @@
             pw.print(r);
             pw.print(" pid=");
             if (r.proc != null) {
-                pw.println(r.proc.pid);
+                pw.println(r.proc.getPid());
             } else {
                 pw.println("(not running)");
             }
@@ -394,10 +397,10 @@
                 r.dump(pw, innerPrefix, true);
             }
         }
-        if (r.proc != null && r.proc.thread != null) {
+        if (thread != null) {
             pw.println("    Client:");
             pw.flush();
-            dumpToTransferPipe("      ", fd, pw, r, args);
+            dumpToTransferPipe("      ", fd, pw, r, thread, args);
         }
     }
 
@@ -420,8 +423,9 @@
         // Only dump the first provider, since we are dumping in proto format
         for (int i = 0; i < providers.size(); i++) {
             final ContentProviderRecord r = providers.get(i);
-            if (r.proc != null && r.proc.thread != null) {
-                dumpToTransferPipe(null, fd, pw, r, newArgs);
+            IApplicationThread thread;
+            if (r.proc != null && (thread = r.proc.getThread()) != null) {
+                dumpToTransferPipe(null, fd, pw, r, thread, newArgs);
                 return true;
             }
         }
@@ -433,11 +437,11 @@
      * any meta string (e.g., provider info, indentation) written to the file descriptor.
      */
     private void dumpToTransferPipe(String prefix, FileDescriptor fd, PrintWriter pw,
-            final ContentProviderRecord r, String[] args) {
+            final ContentProviderRecord r, final IApplicationThread thread, String[] args) {
         try {
             TransferPipe tp = new TransferPipe();
             try {
-                r.proc.thread.dumpProvider(
+                thread.dumpProvider(
                     tp.getWriteFd(), r.provider.asBinder(), args);
                 tp.setBufferPrefix(prefix);
                 // Short timeout, since blocking here can
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 8a1b4e3..485087d 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -23,6 +23,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.annotation.Nullable;
+import android.app.IApplicationThread;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -281,7 +282,7 @@
         proto.write(ServiceRecordProto.SHORT_NAME, this.shortInstanceName);
         proto.write(ServiceRecordProto.IS_RUNNING, app != null);
         if (app != null) {
-            proto.write(ServiceRecordProto.PID, app.pid);
+            proto.write(ServiceRecordProto.PID, app.getPid());
         }
         if (intent != null) {
             intent.getIntent().dumpDebug(proto, ServiceRecordProto.INTENT, false, true, false,
@@ -582,13 +583,14 @@
         restartTracker.setRestarting(true, memFactor, now);
     }
 
-    public void setProcess(ProcessRecord _proc) {
-        if (_proc != null) {
+    public void setProcess(ProcessRecord proc, IApplicationThread thread, int pid,
+            UidRecord uidRecord) {
+        if (proc != null) {
             // We're starting a new process for this service, but a previous one is allowed to start
             // background activities. Remove that ability now (unless the new process is the same as
             // the previous one, which is a common case).
             if (mAppForAllowingBgActivityStartsByStart != null) {
-                if (mAppForAllowingBgActivityStartsByStart != _proc) {
+                if (mAppForAllowingBgActivityStartsByStart != proc) {
                     mAppForAllowingBgActivityStartsByStart
                             .removeAllowBackgroundActivityStartsToken(this);
                     ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback);
@@ -596,34 +598,35 @@
             }
             // Make sure the cleanup callback knows about the new process.
             mAppForAllowingBgActivityStartsByStart = mIsAllowedBgActivityStartsByStart
-                    ? _proc : null;
+                    ? proc : null;
             if (mIsAllowedBgActivityStartsByStart
                     || mIsAllowedBgActivityStartsByBinding) {
-                _proc.addOrUpdateAllowBackgroundActivityStartsToken(this,
+                proc.addOrUpdateAllowBackgroundActivityStartsToken(this,
                         getExclusiveOriginatingToken());
             } else {
-                _proc.removeAllowBackgroundActivityStartsToken(this);
+                proc.removeAllowBackgroundActivityStartsToken(this);
             }
             if (mIsAllowedBgFgsStartsByBinding) {
-                _proc.addAllowBackgroundFgsStartsToken(this);
+                proc.mState.addAllowBackgroundFgsStartsToken(this);
             } else {
-                _proc.removeAllowBackgroundFgsStartsToken(this);
+                proc.mState.removeAllowBackgroundFgsStartsToken(this);
             }
         }
-        if (app != null && app != _proc) {
+        if (app != null && app != proc) {
             // If the old app is allowed to start bg activities because of a service start, leave it
             // that way until the cleanup callback runs. Otherwise we can remove its bg activity
             // start ability immediately (it can't be bound now).
             if (!mIsAllowedBgActivityStartsByStart) {
                 app.removeAllowBackgroundActivityStartsToken(this);
             }
-            app.updateBoundClientUids();
+            app.mServices.updateBoundClientUids();
         }
-        app = _proc;
-        if (pendingConnectionGroup > 0 && _proc != null) {
-            _proc.connectionService = this;
-            _proc.connectionGroup = pendingConnectionGroup;
-            _proc.connectionImportance = pendingConnectionImportance;
+        app = proc;
+        if (pendingConnectionGroup > 0 && proc != null) {
+            final ProcessServiceRecord psr = proc.mServices;
+            psr.setConnectionService(this);
+            psr.setConnectionGroup(pendingConnectionGroup);
+            psr.setConnectionImportance(pendingConnectionImportance);
             pendingConnectionGroup = pendingConnectionImportance = 0;
         }
         if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS) {
@@ -631,7 +634,7 @@
                 ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
                 for (int i = 0; i < cr.size(); i++) {
                     final ConnectionRecord conn = cr.get(i);
-                    if (_proc != null) {
+                    if (proc != null) {
                         conn.startAssociationIfNeeded();
                     } else {
                         conn.stopAssociation();
@@ -639,8 +642,8 @@
                 }
             }
         }
-        if (_proc != null) {
-            _proc.updateBoundClientUids();
+        if (proc != null) {
+            proc.mServices.updateBoundClientUids();
         }
     }
 
@@ -658,7 +661,7 @@
 
         // if we have a process attached, add bound client uid of this connection to it
         if (app != null) {
-            app.addBoundClientUid(c.clientUid);
+            app.mServices.addBoundClientUid(c.clientUid);
         }
     }
 
@@ -666,7 +669,7 @@
         connections.remove(binder);
         // if we have a process attached, tell it to update the state of bound clients
         if (app != null) {
-            app.updateBoundClientUids();
+            app.mServices.updateBoundClientUids();
         }
     }
 
@@ -820,9 +823,9 @@
             return;
         }
         if (mIsAllowedBgFgsStartsByBinding) {
-            app.addAllowBackgroundFgsStartsToken(this);
+            app.mState.addAllowBackgroundFgsStartsToken(this);
         } else {
-            app.removeAllowBackgroundFgsStartsToken(this);
+            app.mState.removeAllowBackgroundFgsStartsToken(this);
         }
     }
 
@@ -937,7 +940,7 @@
 
     public void postNotification() {
         final int appUid = appInfo.uid;
-        final int appPid = app.pid;
+        final int appPid = app.getPid();
         if (foregroundId != 0 && foregroundNoti != null) {
             // Do asynchronous communication with notification manager to
             // avoid deadlocks.
@@ -1057,7 +1060,7 @@
         final String localPackageName = packageName;
         final int localForegroundId = foregroundId;
         final int appUid = appInfo.uid;
-        final int appPid = app != null ? app.pid : 0;
+        final int appPid = app != null ? app.getPid() : 0;
         ams.mHandler.post(new Runnable() {
             public void run() {
                 NotificationManagerInternal nm = LocalServices.getService(
diff --git a/services/core/java/com/android/server/am/StrictModeViolationDialog.java b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
index 22e7faa..4cc1fc1 100644
--- a/services/core/java/com/android/server/am/StrictModeViolationDialog.java
+++ b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
@@ -49,7 +49,7 @@
         mProc = app;
         mResult = result;
         CharSequence name;
-        if ((app.getPkgList().size() == 1)
+        if (app.getPkgList().size() == 1
                 && (name = context.getPackageManager().getApplicationLabel(app.info)) != null) {
             setMessage(res.getString(
                     com.android.internal.R.string.smv_application,
@@ -67,7 +67,7 @@
                   res.getText(com.android.internal.R.string.dlg_ok),
                   mHandler.obtainMessage(ACTION_OK));
 
-        if (app.errorReportReceiver != null) {
+        if (app.mErrorState.getErrorReportReceiver() != null) {
             setButton(DialogInterface.BUTTON_NEGATIVE,
                       res.getText(com.android.internal.R.string.report),
                       mHandler.obtainMessage(ACTION_OK_AND_REPORT));
@@ -84,9 +84,9 @@
 
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
-            synchronized (mService) {
+            synchronized (mService.mProcLock) {
                 if (mProc != null) {
-                    mProc.getDialogController().clearViolationDialogs();
+                    mProc.mErrorState.getDialogController().clearViolationDialogs();
                 }
             }
             mResult.set(msg.what);
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index b3150d1..df65ee6 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -204,16 +204,18 @@
                 } else {
                     UidRecord validateUid = mValidateUids.get(item.uid);
                     if (validateUid == null) {
-                        validateUid = new UidRecord(item.uid);
+                        validateUid = new UidRecord(item.uid, null);
                         mValidateUids.put(item.uid, validateUid);
                     }
                     if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
-                        validateUid.idle = true;
+                        validateUid.setIdle(true);
                     } else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
-                        validateUid.idle = false;
+                        validateUid.setIdle(false);
                     }
-                    validateUid.setCurProcState(validateUid.setProcState = item.procState);
-                    validateUid.curCapability = validateUid.setCapability = item.capability;
+                    validateUid.setSetProcState(item.procState);
+                    validateUid.setCurProcState(item.procState);
+                    validateUid.setSetCapability(item.capability);
+                    validateUid.setCurCapability(item.capability);
                     validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
                 }
             }
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 36d22bf..2fb662f 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -26,27 +26,58 @@
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
 
+import com.android.internal.annotations.CompositeRWLock;
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.am.UidObserverController.ChangeRecord;
 
+import java.util.function.Consumer;
+
 /**
  * Overall information about a uid that has actively running processes.
  */
 public final class UidRecord {
-    final int uid;
-    int mCurProcState;
-    int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
-    int curCapability;
-    int setCapability;
-    long lastBackgroundTime;
-    boolean ephemeral;
-    boolean foregroundServices;
-    boolean mCurAllowlist;
-    boolean mSetAllowlist;
-    boolean idle;
-    boolean setIdle;
-    int numProcs;
-    ArraySet<ProcessRecord> procRecords = new ArraySet<>();
+    private final ActivityManagerService mService;
+    private final ActivityManagerGlobalLock mProcLock;
+    private final int mUid;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mCurProcState;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mSetProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mCurCapability;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mSetCapability;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private long mLastBackgroundTime;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mEphemeral;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mForegroundServices;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mCurAllowList;;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mSetAllowList;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mIdle;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mSetIdle;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private int mNumProcs;
+
+    @CompositeRWLock({"mService", "mProcLock"})
+    private ArraySet<ProcessRecord> mProcRecords = new ArraySet<>();
 
     /**
      * Sequence number associated with the {@link #mCurProcState}. This is incremented using
@@ -111,32 +142,168 @@
     // UidObserverController is the only thing that should modify this.
     final ChangeRecord pendingChange = new ChangeRecord();
 
-    int lastReportedChange;
+    @GuardedBy("mService")
+    private int mLastReportedChange;
 
-    public UidRecord(int _uid) {
-        uid = _uid;
-        idle = true;
+    public UidRecord(int uid, ActivityManagerService service) {
+        mUid = uid;
+        mService = service;
+        mProcLock = service != null ? service.mProcLock : null;
+        mIdle = true;
         reset();
     }
 
-    public int getCurProcState() {
+    int getUid() {
+        return mUid;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getCurProcState() {
         return mCurProcState;
     }
 
-    public void setCurProcState(int curProcState) {
+    @GuardedBy({"mService", "mProcLock"})
+    void setCurProcState(int curProcState) {
         mCurProcState = curProcState;
     }
 
-    public void reset() {
-        setCurProcState(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
-        foregroundServices = false;
-        curCapability = 0;
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getSetProcState() {
+        return mSetProcState;
+    }
 
+    @GuardedBy({"mService", "mProcLock"})
+    void setSetProcState(int setProcState) {
+        mSetProcState = setProcState;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getCurCapability() {
+        return mCurCapability;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setCurCapability(int curCapability) {
+        mCurCapability = curCapability;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getSetCapability() {
+        return mSetCapability;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setSetCapability(int setCapability) {
+        mSetCapability = setCapability;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    long getLastBackgroundTime() {
+        return mLastBackgroundTime;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setLastBackgroundTime(long lastBackgroundTime) {
+        mLastBackgroundTime = lastBackgroundTime;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isEphemeral() {
+        return mEphemeral;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setEphemeral(boolean ephemeral) {
+        mEphemeral = ephemeral;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean hasForegroundServices() {
+        return mForegroundServices;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setForegroundServices(boolean foregroundServices) {
+        mForegroundServices = foregroundServices;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isCurAllowListed() {
+        return mCurAllowList;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setCurAllowListed(boolean curAllowList) {
+        mCurAllowList = curAllowList;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isSetAllowListed() {
+        return mSetAllowList;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setSetAllowListed(boolean setAllowlist) {
+        mSetAllowList = setAllowlist;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isIdle() {
+        return mIdle;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setIdle(boolean idle) {
+        mIdle = idle;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean isSetIdle() {
+        return mSetIdle;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void setSetIdle(boolean setIdle) {
+        mSetIdle = setIdle;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getNumOfProcs() {
+        return mProcRecords.size();
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    void forEachProcess(Consumer<ProcessRecord> callback) {
+        for (int i = mProcRecords.size() - 1; i >= 0; i--) {
+            callback.accept(mProcRecords.valueAt(i));
+        }
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void addProcess(ProcessRecord app) {
+        mProcRecords.add(app);
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void removeProcess(ProcessRecord app) {
+        mProcRecords.remove(app);
+    }
+
+    @GuardedBy("mService")
+    void setLastReportedChange(int lastReportedChange) {
+        mLastReportedChange = lastReportedChange;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void reset() {
+        setCurProcState(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+        mForegroundServices = false;
+        mCurCapability = 0;
     }
 
     public void updateHasInternetPermission() {
         hasInternetPermission = ActivityManager.checkUidPermission(Manifest.permission.INTERNET,
-                uid) == PackageManager.PERMISSION_GRANTED;
+                mUid) == PackageManager.PERMISSION_GRANTED;
     }
 
     /**
@@ -153,19 +320,19 @@
 
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
-        proto.write(UidRecordProto.UID, uid);
+        proto.write(UidRecordProto.UID, mUid);
         proto.write(UidRecordProto.CURRENT, ProcessList.makeProcStateProtoEnum(mCurProcState));
-        proto.write(UidRecordProto.EPHEMERAL, ephemeral);
-        proto.write(UidRecordProto.FG_SERVICES, foregroundServices);
-        proto.write(UidRecordProto.WHILELIST, mCurAllowlist);
+        proto.write(UidRecordProto.EPHEMERAL, mEphemeral);
+        proto.write(UidRecordProto.FG_SERVICES, mForegroundServices);
+        proto.write(UidRecordProto.WHILELIST, mCurAllowList);
         ProtoUtils.toDuration(proto, UidRecordProto.LAST_BACKGROUND_TIME,
-                lastBackgroundTime, SystemClock.elapsedRealtime());
-        proto.write(UidRecordProto.IDLE, idle);
-        if (lastReportedChange != 0) {
+                mLastBackgroundTime, SystemClock.elapsedRealtime());
+        proto.write(UidRecordProto.IDLE, mIdle);
+        if (mLastReportedChange != 0) {
             ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidRecordProto.LAST_REPORTED_CHANGES,
-                    lastReportedChange, ORIG_ENUMS, PROTO_ENUMS);
+                    mLastReportedChange, ORIG_ENUMS, PROTO_ENUMS);
         }
-        proto.write(UidRecordProto.NUM_PROCS, numProcs);
+        proto.write(UidRecordProto.NUM_PROCS, mNumProcs);
 
         long seqToken = proto.start(UidRecordProto.NETWORK_STATE_UPDATE);
         proto.write(UidRecordProto.ProcStateSequence.CURURENT, curProcStateSeq);
@@ -182,54 +349,54 @@
         sb.append("UidRecord{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
         sb.append(' ');
-        UserHandle.formatUid(sb, uid);
+        UserHandle.formatUid(sb, mUid);
         sb.append(' ');
         sb.append(ProcessList.makeProcStateString(mCurProcState));
-        if (ephemeral) {
+        if (mEphemeral) {
             sb.append(" ephemeral");
         }
-        if (foregroundServices) {
+        if (mForegroundServices) {
             sb.append(" fgServices");
         }
-        if (mCurAllowlist) {
+        if (mCurAllowList) {
             sb.append(" allowlist");
         }
-        if (lastBackgroundTime > 0) {
+        if (mLastBackgroundTime > 0) {
             sb.append(" bg:");
-            TimeUtils.formatDuration(SystemClock.elapsedRealtime()-lastBackgroundTime, sb);
+            TimeUtils.formatDuration(SystemClock.elapsedRealtime() - mLastBackgroundTime, sb);
         }
-        if (idle) {
+        if (mIdle) {
             sb.append(" idle");
         }
-        if (lastReportedChange != 0) {
+        if (mLastReportedChange != 0) {
             sb.append(" change:");
             boolean printed = false;
-            if ((lastReportedChange & CHANGE_GONE) != 0) {
+            if ((mLastReportedChange & CHANGE_GONE) != 0) {
                 printed = true;
                 sb.append("gone");
             }
-            if ((lastReportedChange & CHANGE_IDLE) != 0) {
+            if ((mLastReportedChange & CHANGE_IDLE) != 0) {
                 if (printed) {
                     sb.append("|");
                 }
                 printed = true;
                 sb.append("idle");
             }
-            if ((lastReportedChange & CHANGE_ACTIVE) != 0) {
+            if ((mLastReportedChange & CHANGE_ACTIVE) != 0) {
                 if (printed) {
                     sb.append("|");
                 }
                 printed = true;
                 sb.append("active");
             }
-            if ((lastReportedChange & CHANGE_CACHED) != 0) {
+            if ((mLastReportedChange & CHANGE_CACHED) != 0) {
                 if (printed) {
                     sb.append("|");
                 }
                 printed = true;
                 sb.append("cached");
             }
-            if ((lastReportedChange & CHANGE_UNCACHED) != 0) {
+            if ((mLastReportedChange & CHANGE_UNCACHED) != 0) {
                 if (printed) {
                     sb.append("|");
                 }
@@ -237,7 +404,7 @@
             }
         }
         sb.append(" procs:");
-        sb.append(numProcs);
+        sb.append(mNumProcs);
         sb.append(" seq(");
         sb.append(curProcStateSeq);
         sb.append(",");
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index fded85c..e97f0b4 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -18,11 +18,9 @@
 
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-import static android.content.Intent.ACTION_USER_ADDED;
-import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS;
 import static android.content.Intent.EXTRA_REPLACING;
-import static android.content.pm.PackageManager.MATCH_ALL;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
 
 import android.annotation.NonNull;
@@ -36,8 +34,9 @@
 import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
-import android.content.pm.UserInfo;
+import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -48,16 +47,21 @@
 import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.SystemService;
 
+import java.io.File;
 import java.io.FileDescriptor;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 
 /**
  * System service that manages app hibernation state, a state apps can enter that means they are
@@ -66,6 +70,11 @@
  */
 public final class AppHibernationService extends SystemService {
     private static final String TAG = "AppHibernationService";
+    private static final int PACKAGE_MATCH_FLAGS =
+            PackageManager.MATCH_DIRECT_BOOT_AWARE
+                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                    | PackageManager.MATCH_UNINSTALLED_PACKAGES
+                    | PackageManager.MATCH_DISABLED_COMPONENTS;
 
     /**
      * Lock for accessing any in-memory hibernation state
@@ -76,9 +85,13 @@
     private final IActivityManager mIActivityManager;
     private final UserManager mUserManager;
     @GuardedBy("mLock")
-    private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>();
+    private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>();
+    private final SparseArray<HibernationStateDiskStore<UserLevelState>> mUserDiskStores =
+            new SparseArray<>();
     @GuardedBy("mLock")
-    private final Set<String> mGloballyHibernatedPackages = new ArraySet<>();
+    private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>();
+    private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore;
+    private final Injector mInjector;
 
     /**
      * Initializes the system service.
@@ -90,28 +103,22 @@
      * @param context The system server context.
      */
     public AppHibernationService(@NonNull Context context) {
-        this(context, IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
-                ActivityManager.getService(),
-                context.getSystemService(UserManager.class));
+        this(new InjectorImpl(context));
     }
 
     @VisibleForTesting
-    AppHibernationService(@NonNull Context context, IPackageManager packageManager,
-            IActivityManager activityManager, UserManager userManager) {
-        super(context);
-        mContext = context;
-        mIPackageManager = packageManager;
-        mIActivityManager = activityManager;
-        mUserManager = userManager;
+    AppHibernationService(@NonNull Injector injector) {
+        super(injector.getContext());
+        mContext = injector.getContext();
+        mIPackageManager = injector.getPackageManager();
+        mIActivityManager = injector.getActivityManager();
+        mUserManager = injector.getUserManager();
+        mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
+        mInjector = injector;
 
         final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
 
         IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(ACTION_USER_ADDED);
-        intentFilter.addAction(ACTION_USER_REMOVED);
-        userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
-
-        intentFilter = new IntentFilter();
         intentFilter.addAction(ACTION_PACKAGE_ADDED);
         intentFilter.addAction(ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
@@ -126,12 +133,10 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == PHASE_BOOT_COMPLETED) {
+            List<GlobalLevelState> states =
+                    mGlobalLevelHibernationDiskStore.readHibernationStates();
             synchronized (mLock) {
-                final List<UserInfo> users = mUserManager.getUsers();
-                // TODO: Pull from persistent disk storage. For now, just make from scratch.
-                for (UserInfo user : users) {
-                    addUserPackageStatesL(user.id);
-                }
+                initializeGlobalHibernationStates(states);
             }
         }
     }
@@ -145,12 +150,14 @@
      */
     boolean isHibernatingForUser(String packageName, int userId) {
         userId = handleIncomingUser(userId, "isHibernating");
+        if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+            Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user "
+                    + userId);
+            return false;
+        }
         synchronized (mLock) {
-            final Map<String, UserPackageState> packageStates = mUserStates.get(userId);
-            if (packageStates == null) {
-                throw new IllegalArgumentException("No user associated with user id " + userId);
-            }
-            final UserPackageState pkgState = packageStates.get(packageName);
+            final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
+            final UserLevelState pkgState = packageStates.get(packageName);
             if (pkgState == null) {
                 throw new IllegalArgumentException(
                         String.format("Package %s is not installed for user %s",
@@ -168,7 +175,12 @@
      */
     boolean isHibernatingGlobally(String packageName) {
         synchronized (mLock) {
-            return mGloballyHibernatedPackages.contains(packageName);
+            GlobalLevelState state = mGlobalHibernationStates.get(packageName);
+            if (state == null) {
+                throw new IllegalArgumentException(
+                        String.format("Package %s is not installed", packageName));
+            }
+            return state.hibernated;
         }
     }
 
@@ -181,12 +193,14 @@
      */
     void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
         userId = handleIncomingUser(userId, "setHibernating");
+        if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+            Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user "
+                    + userId);
+            return;
+        }
         synchronized (mLock) {
-            if (!mUserStates.contains(userId)) {
-                throw new IllegalArgumentException("No user associated with user id " + userId);
-            }
-            Map<String, UserPackageState> packageStates = mUserStates.get(userId);
-            UserPackageState pkgState = packageStates.get(packageName);
+            final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
+            final UserLevelState pkgState = packageStates.get(packageName);
             if (pkgState == null) {
                 throw new IllegalArgumentException(
                         String.format("Package %s is not installed for user %s",
@@ -198,10 +212,12 @@
             }
 
             if (isHibernating) {
-                hibernatePackageForUserL(packageName, userId, pkgState);
+                hibernatePackageForUser(packageName, userId, pkgState);
             } else {
-                unhibernatePackageForUserL(packageName, userId, pkgState);
+                unhibernatePackageForUser(packageName, userId, pkgState);
             }
+            List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
+            mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
         }
     }
 
@@ -213,25 +229,32 @@
      * @param isHibernating new hibernation state
      */
     void setHibernatingGlobally(String packageName, boolean isHibernating) {
-        if (isHibernating != mGloballyHibernatedPackages.contains(packageName)) {
-            synchronized (mLock) {
+        synchronized (mLock) {
+            GlobalLevelState state = mGlobalHibernationStates.get(packageName);
+            if (state == null) {
+                throw new IllegalArgumentException(
+                        String.format("Package %s is not installed for any user", packageName));
+            }
+            if (state.hibernated != isHibernating) {
                 if (isHibernating) {
-                    hibernatePackageGloballyL(packageName);
+                    hibernatePackageGlobally(packageName, state);
                 } else {
-                    unhibernatePackageGloballyL(packageName);
+                    unhibernatePackageGlobally(packageName, state);
                 }
+                List<GlobalLevelState> states = new ArrayList<>(mGlobalHibernationStates.values());
+                mGlobalLevelHibernationDiskStore.scheduleWriteHibernationStates(states);
             }
         }
     }
 
     /**
      * Put an app into hibernation for a given user, allowing user-level optimizations to occur.
-     * The caller should hold {@link #mLock}
      *
      * @param pkgState package hibernation state
      */
-    private void hibernatePackageForUserL(@NonNull String packageName, int userId,
-            @NonNull UserPackageState pkgState) {
+    @GuardedBy("mLock")
+    private void hibernatePackageForUser(@NonNull String packageName, int userId,
+            @NonNull UserLevelState pkgState) {
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
         final long caller = Binder.clearCallingIdentity();
         try {
@@ -249,12 +272,13 @@
     }
 
     /**
-     * Remove a package from hibernation for a given user. The caller should hold {@link #mLock}.
+     * Remove a package from hibernation for a given user.
      *
      * @param pkgState package hibernation state
      */
-    private void unhibernatePackageForUserL(@NonNull String packageName, int userId,
-            UserPackageState pkgState) {
+    @GuardedBy("mLock")
+    private void unhibernatePackageForUser(@NonNull String packageName, int userId,
+            UserLevelState pkgState) {
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
         final long caller = Binder.clearCallingIdentity();
         try {
@@ -271,60 +295,140 @@
 
     /**
      * Put a package into global hibernation, optimizing its storage at a package / APK level.
-     * The caller should hold {@link #mLock}.
      */
-    private void hibernatePackageGloballyL(@NonNull String packageName) {
+    @GuardedBy("mLock")
+    private void hibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
         // TODO(175830194): Delete vdex/odex when DexManager API is built out
-        mGloballyHibernatedPackages.add(packageName);
+        state.hibernated = true;
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
     }
 
     /**
-     * Unhibernate a package from global hibernation. The caller should hold {@link #mLock}.
+     * Unhibernate a package from global hibernation.
      */
-    private void unhibernatePackageGloballyL(@NonNull String packageName) {
+    @GuardedBy("mLock")
+    private void unhibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally");
-        mGloballyHibernatedPackages.remove(packageName);
+        state.hibernated = false;
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
     }
 
     /**
-     * Populates {@link #mUserStates} with the users installed packages. The caller should hold
-     * {@link #mLock}.
+     * Initializes in-memory store of user-level hibernation states for the given user
      *
      * @param userId user id to add installed packages for
+     * @param diskStates states pulled from disk, if available
      */
-    private void addUserPackageStatesL(int userId) {
-        Map<String, UserPackageState> packages = new ArrayMap<>();
-        List<PackageInfo> packageList;
+    @GuardedBy("mLock")
+    private void initializeUserHibernationStates(int userId,
+            @Nullable List<UserLevelState> diskStates) {
+        List<PackageInfo> packages;
         try {
-            packageList = mIPackageManager.getInstalledPackages(MATCH_ALL, userId).getList();
+            packages = mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId).getList();
         } catch (RemoteException e) {
-            throw new IllegalStateException("Package manager not available.", e);
+            throw new IllegalStateException("Package manager not available", e);
         }
 
-        for (int i = 0, size = packageList.size(); i < size; i++) {
-            packages.put(packageList.get(i).packageName, new UserPackageState());
+        Map<String, UserLevelState> userLevelStates = new ArrayMap<>();
+
+        for (int i = 0, size = packages.size(); i < size; i++) {
+            String packageName = packages.get(i).packageName;
+            UserLevelState state = new UserLevelState();
+            state.packageName = packageName;
+            userLevelStates.put(packageName, state);
         }
-        mUserStates.put(userId, packages);
+
+        if (diskStates != null) {
+            Set<String> installedPackages = new ArraySet<>();
+            for (int i = 0, size = packages.size(); i < size; i++) {
+                installedPackages.add(packages.get(i).packageName);
+            }
+            for (int i = 0, size = diskStates.size(); i < size; i++) {
+                String packageName = diskStates.get(i).packageName;
+                if (!installedPackages.contains(packageName)) {
+                    Slog.w(TAG, String.format(
+                            "No hibernation state associated with package %s user %d. Maybe"
+                                    + "the package was uninstalled? ", packageName, userId));
+                    continue;
+                }
+                userLevelStates.put(packageName, diskStates.get(i));
+            }
+        }
+        mUserStates.put(userId, userLevelStates);
     }
 
-    private void onUserAdded(int userId) {
-        synchronized (mLock) {
-            addUserPackageStatesL(userId);
+    /**
+     * Initialize in-memory store of global level hibernation states.
+     *
+     * @param diskStates global level hibernation states pulled from disk, if available
+     */
+    @GuardedBy("mLock")
+    private void initializeGlobalHibernationStates(@Nullable List<GlobalLevelState> diskStates) {
+        List<PackageInfo> packages;
+        try {
+            packages = mIPackageManager.getInstalledPackages(
+                    PACKAGE_MATCH_FLAGS | MATCH_ANY_USER, 0 /* userId */).getList();
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Package manager not available", e);
+        }
+
+        for (int i = 0, size = packages.size(); i < size; i++) {
+            String packageName = packages.get(i).packageName;
+            GlobalLevelState state = new GlobalLevelState();
+            state.packageName = packageName;
+            mGlobalHibernationStates.put(packageName, state);
+        }
+        if (diskStates != null) {
+            Set<String> installedPackages = new ArraySet<>();
+            for (int i = 0, size = packages.size(); i < size; i++) {
+                installedPackages.add(packages.get(i).packageName);
+            }
+            for (int i = 0, size = diskStates.size(); i < size; i++) {
+                GlobalLevelState state = diskStates.get(i);
+                if (!installedPackages.contains(state.packageName)) {
+                    Slog.w(TAG, String.format(
+                            "No hibernation state associated with package %s. Maybe the "
+                                    + "package was uninstalled? ", state.packageName));
+                    continue;
+                }
+                mGlobalHibernationStates.put(state.packageName, state);
+            }
         }
     }
 
-    private void onUserRemoved(int userId) {
+    @Override
+    public void onUserUnlocking(@NonNull TargetUser user) {
+        int userId = user.getUserIdentifier();
+        HibernationStateDiskStore<UserLevelState> diskStore =
+                mInjector.getUserLevelDiskStore(userId);
+        mUserDiskStores.put(userId, diskStore);
+        List<UserLevelState> storedStates = diskStore.readHibernationStates();
         synchronized (mLock) {
+            initializeUserHibernationStates(userId, storedStates);
+        }
+    }
+
+    @Override
+    public void onUserStopping(@NonNull TargetUser user) {
+        int userId = user.getUserIdentifier();
+        // TODO: Flush any scheduled writes to disk immediately on user stopping / power off.
+        synchronized (mLock) {
+            mUserDiskStores.remove(userId);
             mUserStates.remove(userId);
         }
     }
 
     private void onPackageAdded(@NonNull String packageName, int userId) {
         synchronized (mLock) {
-            mUserStates.get(userId).put(packageName, new UserPackageState());
+            UserLevelState userState = new UserLevelState();
+            userState.packageName = packageName;
+            mUserStates.get(userId).put(packageName, userState);
+            if (!mGlobalHibernationStates.containsKey(packageName)) {
+                GlobalLevelState globalState = new GlobalLevelState();
+                globalState.packageName = packageName;
+                mGlobalHibernationStates.put(packageName, globalState);
+            }
         }
     }
 
@@ -336,7 +440,7 @@
 
     private void onPackageRemovedForAllUsers(@NonNull String packageName) {
         synchronized (mLock) {
-            mGloballyHibernatedPackages.remove(packageName);
+            mGlobalHibernationStates.remove(packageName);
         }
     }
 
@@ -395,7 +499,7 @@
         }
     }
 
-    // Broadcast receiver for user and package add/removal events
+    // Broadcast receiver for package add/removal events
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -405,12 +509,6 @@
             }
 
             final String action = intent.getAction();
-            if (ACTION_USER_ADDED.equals(action)) {
-                onUserAdded(userId);
-            }
-            if (ACTION_USER_REMOVED.equals(action)) {
-                onUserRemoved(userId);
-            }
             if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) {
                 final String packageName = intent.getData().getSchemeSpecificPart();
                 if (intent.getBooleanExtra(EXTRA_REPLACING, false)) {
@@ -443,10 +541,66 @@
     }
 
     /**
-     * Data class that contains hibernation state info of a package for a user.
+     * Dependency injector for {@link #AppHibernationService)}.
      */
-    private static final class UserPackageState {
-        public boolean hibernated;
-        // TODO: Track whether hibernation is exempted by the user
+    interface Injector {
+        Context getContext();
+
+        IPackageManager getPackageManager();
+
+        IActivityManager getActivityManager();
+
+        UserManager getUserManager();
+
+        HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();
+
+        HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
+    }
+
+    private static final class InjectorImpl implements Injector {
+        private static final String HIBERNATION_DIR_NAME = "hibernation";
+        private final Context mContext;
+        private final ScheduledExecutorService mScheduledExecutorService;
+        private final UserLevelHibernationProto mUserLevelHibernationProto;
+
+        InjectorImpl(Context context) {
+            mContext = context;
+            mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+            mUserLevelHibernationProto = new UserLevelHibernationProto();
+        }
+
+        @Override
+        public Context getContext() {
+            return mContext;
+        }
+
+        @Override
+        public IPackageManager getPackageManager() {
+            return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+        }
+
+        @Override
+        public IActivityManager getActivityManager() {
+            return ActivityManager.getService();
+        }
+
+        @Override
+        public UserManager getUserManager() {
+            return mContext.getSystemService(UserManager.class);
+        }
+
+        @Override
+        public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
+            File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME);
+            return new HibernationStateDiskStore<>(
+                    dir, new GlobalLevelHibernationProto(), mScheduledExecutorService);
+        }
+
+        @Override
+        public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
+            File dir = new File(Environment.getDataSystemCeDirectory(userId), HIBERNATION_DIR_NAME);
+            return new HibernationStateDiskStore<>(
+                    dir, mUserLevelHibernationProto, mScheduledExecutorService);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
new file mode 100644
index 0000000..79e995b
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads and writes protos for {@link GlobalLevelState} hiberation states.
+ */
+final class GlobalLevelHibernationProto implements ProtoReadWriter<List<GlobalLevelState>> {
+    private static final String TAG = "GlobalLevelHibernationProtoReadWriter";
+
+    @Override
+    public void writeToProto(@NonNull ProtoOutputStream stream,
+            @NonNull List<GlobalLevelState> data) {
+        for (int i = 0, size = data.size(); i < size; i++) {
+            long token = stream.start(GlobalLevelHibernationStatesProto.HIBERNATION_STATE);
+            GlobalLevelState state = data.get(i);
+            stream.write(GlobalLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
+            stream.write(GlobalLevelHibernationStateProto.HIBERNATED, state.hibernated);
+            stream.end(token);
+        }
+    }
+
+    @Override
+    public @Nullable List<GlobalLevelState> readFromProto(@NonNull ProtoInputStream stream)
+            throws IOException {
+        List<GlobalLevelState> list = new ArrayList<>();
+        while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+            if (stream.getFieldNumber()
+                    != (int) GlobalLevelHibernationStatesProto.HIBERNATION_STATE) {
+                continue;
+            }
+            GlobalLevelState state = new GlobalLevelState();
+            long token = stream.start(GlobalLevelHibernationStatesProto.HIBERNATION_STATE);
+            while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                switch (stream.getFieldNumber()) {
+                    case (int) GlobalLevelHibernationStateProto.PACKAGE_NAME:
+                        state.packageName =
+                                stream.readString(GlobalLevelHibernationStateProto.PACKAGE_NAME);
+                        break;
+                    case (int) GlobalLevelHibernationStateProto.HIBERNATED:
+                        state.hibernated =
+                                stream.readBoolean(GlobalLevelHibernationStateProto.HIBERNATED);
+                        break;
+                    default:
+                        Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
+                }
+            }
+            stream.end(token);
+            list.add(state);
+        }
+        return list;
+    }
+}
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
similarity index 73%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to services/core/java/com/android/server/apphibernation/GlobalLevelState.java
index 19b20f2..4f75675 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
-package android.graphics.fonts;
+package com.android.server.apphibernation;
 
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/**
+ * Data class that contains global hibernation state for a package.
+ */
+final class GlobalLevelState {
+    public String packageName;
+    public boolean hibernated;
+}
diff --git a/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
new file mode 100644
index 0000000..c83659d
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.text.format.DateUtils;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Disk store utility class for hibernation states.
+ *
+ * @param <T> the type of hibernation state data
+ */
+class HibernationStateDiskStore<T> {
+    private static final String TAG = "HibernationStateDiskStore";
+
+    // Time to wait before actually writing. Saves extra writes if data changes come in batches.
+    private static final long DISK_WRITE_DELAY = 1L * DateUtils.MINUTE_IN_MILLIS;
+    private static final String STATES_FILE_NAME = "states";
+
+    private final File mHibernationFile;
+    private final ScheduledExecutorService mExecutorService;
+    private final ProtoReadWriter<List<T>> mProtoReadWriter;
+    private List<T> mScheduledStatesToWrite = new ArrayList<>();
+    private ScheduledFuture<?> mFuture;
+
+    /**
+     * Initialize a disk store for hibernation states in the given directory.
+     *
+     * @param hibernationDir directory to write/read states file
+     * @param readWriter writer/reader of states proto
+     * @param executorService scheduled executor for writing data
+     */
+    HibernationStateDiskStore(@NonNull File hibernationDir,
+            @NonNull ProtoReadWriter<List<T>> readWriter,
+            @NonNull ScheduledExecutorService executorService) {
+        this(hibernationDir, readWriter, executorService, STATES_FILE_NAME);
+    }
+
+    @VisibleForTesting
+    HibernationStateDiskStore(@NonNull File hibernationDir,
+            @NonNull ProtoReadWriter<List<T>> readWriter,
+            @NonNull ScheduledExecutorService executorService,
+            @NonNull String fileName) {
+        mHibernationFile = new File(hibernationDir, fileName);
+        mExecutorService = executorService;
+        mProtoReadWriter = readWriter;
+    }
+
+    /**
+     * Schedule a full write of all the hibernation states to the file on disk. Does not run
+     * immediately and subsequent writes override previous ones.
+     *
+     * @param hibernationStates list of hibernation states to write to disk
+     */
+    void scheduleWriteHibernationStates(@NonNull List<T> hibernationStates) {
+        synchronized (this) {
+            mScheduledStatesToWrite = hibernationStates;
+            if (mExecutorService.isShutdown()) {
+                Slog.e(TAG, "Scheduled executor service is shut down.");
+                return;
+            }
+
+            // Already have write scheduled
+            if (mFuture != null) {
+                Slog.i(TAG, "Write already scheduled. Skipping schedule.");
+                return;
+            }
+
+            mFuture = mExecutorService.schedule(this::writeHibernationStates, DISK_WRITE_DELAY,
+                    TimeUnit.MILLISECONDS);
+        }
+    }
+
+    /**
+     * Read hibernation states from disk.
+     *
+     * @return the parsed list of hibernation states, null if file does not exist
+     */
+    @Nullable
+    List<T> readHibernationStates() {
+        synchronized (this) {
+            if (!mHibernationFile.exists()) {
+                Slog.i(TAG, "No hibernation file on disk for file " + mHibernationFile.getPath());
+                return null;
+            }
+            AtomicFile atomicFile = new AtomicFile(mHibernationFile);
+
+            try {
+                FileInputStream inputStream = atomicFile.openRead();
+                ProtoInputStream protoInputStream = new ProtoInputStream(inputStream);
+                return mProtoReadWriter.readFromProto(protoInputStream);
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to read states protobuf.", e);
+                return null;
+            }
+        }
+    }
+
+    @WorkerThread
+    private void writeHibernationStates() {
+        synchronized (this) {
+            writeStateProto(mScheduledStatesToWrite);
+            mScheduledStatesToWrite.clear();
+            mFuture = null;
+        }
+    }
+
+    @WorkerThread
+    private void writeStateProto(List<T> states) {
+        AtomicFile atomicFile = new AtomicFile(mHibernationFile);
+
+        FileOutputStream fileOutputStream;
+        try {
+            fileOutputStream = atomicFile.startWrite();
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to start write to states protobuf.", e);
+            return;
+        }
+
+        try {
+            ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream);
+            mProtoReadWriter.writeToProto(protoOutputStream, states);
+            protoOutputStream.flush();
+            atomicFile.finishWrite(fileOutputStream);
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to finish write to states protobuf.", e);
+            atomicFile.failWrite(fileOutputStream);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java b/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java
new file mode 100644
index 0000000..0cbc09a
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+
+/**
+ * Proto utility that reads and writes proto for some data.
+ *
+ * @param <T> data that can be written and read from a proto
+ */
+interface ProtoReadWriter<T> {
+
+    /**
+     * Write data to a proto stream
+     */
+    void writeToProto(@NonNull ProtoOutputStream stream, @NonNull T data);
+
+    /**
+     * Parse data from the proto stream and return
+     */
+    @Nullable T readFromProto(@NonNull ProtoInputStream stream) throws IOException;
+}
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java
new file mode 100644
index 0000000..a24c4c5
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads and writes protos for {@link UserLevelState} hiberation states.
+ */
+final class UserLevelHibernationProto implements ProtoReadWriter<List<UserLevelState>> {
+    private static final String TAG = "UserLevelHibernationProtoReadWriter";
+
+    @Override
+    public void writeToProto(@NonNull ProtoOutputStream stream,
+            @NonNull List<UserLevelState> data) {
+        for (int i = 0, size = data.size(); i < size; i++) {
+            long token = stream.start(UserLevelHibernationStatesProto.HIBERNATION_STATE);
+            UserLevelState state = data.get(i);
+            stream.write(UserLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
+            stream.write(UserLevelHibernationStateProto.HIBERNATED, state.hibernated);
+            stream.end(token);
+        }
+    }
+
+    @Override
+    public @Nullable List<UserLevelState> readFromProto(@NonNull ProtoInputStream stream)
+            throws IOException {
+        List<UserLevelState> list = new ArrayList<>();
+        while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+            if (stream.getFieldNumber()
+                    != (int) UserLevelHibernationStatesProto.HIBERNATION_STATE) {
+                continue;
+            }
+            UserLevelState state = new UserLevelState();
+            long token = stream.start(UserLevelHibernationStatesProto.HIBERNATION_STATE);
+            while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                switch (stream.getFieldNumber()) {
+                    case (int) UserLevelHibernationStateProto.PACKAGE_NAME:
+                        state.packageName =
+                                stream.readString(UserLevelHibernationStateProto.PACKAGE_NAME);
+                        break;
+                    case (int) UserLevelHibernationStateProto.HIBERNATED:
+                        state.hibernated =
+                                stream.readBoolean(UserLevelHibernationStateProto.HIBERNATED);
+                        break;
+                    default:
+                        Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
+                }
+            }
+            stream.end(token);
+            list.add(state);
+        }
+        return list;
+    }
+}
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/services/core/java/com/android/server/apphibernation/UserLevelState.java
similarity index 73%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to services/core/java/com/android/server/apphibernation/UserLevelState.java
index 19b20f2..c66dad8 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
-package android.graphics.fonts;
+package com.android.server.apphibernation;
 
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/**
+ * Data class that contains hibernation state info of a package for a user.
+ */
+final class UserLevelState {
+    public String packageName;
+    public boolean hibernated;
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index f07da8f..10fe1e1 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2796,6 +2796,8 @@
         if (callback == null) {
             return;
         }
+        final boolean mayWatchPackageName =
+                packageName != null && !filterAppAccessUnlocked(packageName);
         synchronized (this) {
             int switchOp = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
 
@@ -2824,7 +2826,7 @@
                 }
                 cbs.add(cb);
             }
-            if (packageName != null) {
+            if (mayWatchPackageName) {
                 ArraySet<ModeCallback> cbs = mPackageModeWatchers.get(packageName);
                 if (cbs == null) {
                     cbs = new ArraySet<>();
@@ -3008,13 +3010,27 @@
         Objects.requireNonNull(packageName);
         try {
             verifyAndGetBypass(uid, packageName, null);
-
+            if (filterAppAccessUnlocked(packageName)) {
+                return AppOpsManager.MODE_ERRORED;
+            }
             return AppOpsManager.MODE_ALLOWED;
         } catch (SecurityException ignored) {
             return AppOpsManager.MODE_ERRORED;
         }
     }
 
+    /**
+     * This method will check with PackageManager to determine if the package provided should
+     * be visible to the {@link Binder#getCallingUid()}.
+     *
+     * NOTE: This must not be called while synchronized on {@code this} to avoid dead locks
+     */
+    private boolean filterAppAccessUnlocked(String packageName) {
+        final int callingUid = Binder.getCallingUid();
+        return LocalServices.getService(PackageManagerInternal.class)
+                .filterAppAccess(packageName, callingUid, UserHandle.getUserId(callingUid));
+    }
+
     @Override
     public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
             String proxiedAttributionTag, int proxyUid, String proxyPackageName,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d575963..e7e9832 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -300,6 +300,7 @@
     private static final int MSG_STREAM_DEVICES_CHANGED = 32;
     private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33;
     private static final int MSG_REINIT_VOLUMES = 34;
+    private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -699,8 +700,9 @@
     private long mLoweredFromNormalToVibrateTime;
 
     // Array of Uids of valid accessibility services to check if caller is one of them
-    private int[] mAccessibilityServiceUids;
     private final Object mAccessibilityServiceUidsLock = new Object();
+    @GuardedBy("mAccessibilityServiceUidsLock")
+    private int[] mAccessibilityServiceUids;
 
     // Uid of the active input method service to check if caller is the one or not.
     private int mInputMethodServiceUid = android.os.Process.INVALID_UID;
@@ -1076,7 +1078,6 @@
 
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
             synchronized (mHdmiClientLock) {
-                mHdmiCecSink = false;
                 mHdmiManager = mContext.getSystemService(HdmiControlManager.class);
                 if (mHdmiManager != null) {
                     mHdmiManager.addHdmiControlStatusChangeListener(
@@ -1508,7 +1509,8 @@
             if (isPlatformTelevision()) {
                 synchronized (mHdmiClientLock) {
                     if (mHdmiManager != null && mHdmiPlaybackClient != null) {
-                        updateHdmiCecSinkLocked(mHdmiCecSink | false);
+                        updateHdmiCecSinkLocked(
+                                mFullVolumeDevices.contains(AudioSystem.DEVICE_OUT_HDMI));
                     }
                 }
             }
@@ -1518,7 +1520,8 @@
             if (isPlatformTelevision()) {
                 synchronized (mHdmiClientLock) {
                     if (mHdmiManager != null) {
-                        updateHdmiCecSinkLocked(mHdmiCecSink | false);
+                        updateHdmiCecSinkLocked(
+                                mFullVolumeDevices.contains(AudioSystem.DEVICE_OUT_HDMI));
                     }
                 }
             }
@@ -2310,11 +2313,9 @@
     /** @see AudioManager#adjustVolume(int, int) */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage, String caller) {
-        boolean hasModifyAudioSettings =
-                mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
-                == PackageManager.PERMISSION_GRANTED;
         adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
-                caller, Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+                caller, Binder.getCallingUid(), callingHasAudioSettingsPermission(),
+                VOL_ADJUST_NORMAL);
     }
 
     public void setFastScrollSoundEffectsEnabled(boolean enabled) {
@@ -2441,13 +2442,12 @@
                     + "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
             return;
         }
-        final boolean hasModifyAudioSettings =
-                mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
-                        == PackageManager.PERMISSION_GRANTED;
+
         sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
                 direction/*val1*/, flags/*val2*/, callingPackage));
         adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
-                Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+                Binder.getCallingUid(), callingHasAudioSettingsPermission(),
+                VOL_ADJUST_NORMAL);
     }
 
     protected void adjustStreamVolume(int streamType, int direction, int flags,
@@ -2670,8 +2670,7 @@
         if (adjustVolume) {
             synchronized (mHdmiClientLock) {
                 if (mHdmiManager != null) {
-                    // mHdmiCecSink true => mHdmiPlaybackClient != null
-                    if (mHdmiCecSink
+                    if (mHdmiPlaybackClient != null
                             && mHdmiCecVolumeControlEnabled
                             && streamTypeAlias == AudioSystem.STREAM_MUSIC
                             // vol change on a full volume device
@@ -2966,13 +2965,11 @@
                     + " MODIFY_AUDIO_ROUTING  callingPackage=" + callingPackage);
             return;
         }
-        final boolean hasModifyAudioSettings =
-                mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
-                        == PackageManager.PERMISSION_GRANTED;
+
         sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
                 index/*val1*/, flags/*val2*/, callingPackage));
         setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
-                Binder.getCallingUid(), hasModifyAudioSettings);
+                Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
     }
 
     private boolean canChangeAccessibilityVolume() {
@@ -3639,8 +3636,7 @@
         ensureValidStreamType(streamType);
         final boolean isPrivileged =
                 Binder.getCallingUid() == Process.SYSTEM_UID
-                 || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
-                        == PackageManager.PERMISSION_GRANTED)
+                 || callingHasAudioSettingsPermission()
                  || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
                         == PackageManager.PERMISSION_GRANTED);
         return (mStreamStates[streamType].getMinIndex(isPrivileged) + 5) / 10;
@@ -4383,13 +4379,10 @@
             throw new SecurityException("Should only be called from system process");
         }
 
-        final boolean hasModifyAudioSettings =
-                mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
-                        == PackageManager.PERMISSION_GRANTED;
         // direction and stream type swap here because the public
         // adjustSuggested has a different order than the other methods.
         adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName, uid,
-                hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+                hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
     }
 
     /** @see AudioManager#adjustStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4407,11 +4400,9 @@
                     new StringBuilder(packageName).append(" uid:").append(uid)
                     .toString()));
         }
-        final boolean hasModifyAudioSettings =
-                mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
-                        == PackageManager.PERMISSION_GRANTED;
+
         adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid,
-                hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+                hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
     }
 
     /** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4423,11 +4414,8 @@
             throw new SecurityException("Should only be called from system process");
         }
 
-        final boolean hasModifyAudioSettings =
-                mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
-                        == PackageManager.PERMISSION_GRANTED;
         setStreamVolume(streamType, index, flags, packageName, packageName, uid,
-                hasModifyAudioSettings);
+                hasAudioSettingsPermission(uid, pid));
     }
 
     //==========================================================================================
@@ -5393,8 +5381,7 @@
     }
 
     boolean checkAudioSettingsPermission(String method) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
-                == PackageManager.PERMISSION_GRANTED) {
+        if (callingOrSelfHasAudioSettingsPermission()) {
             return true;
         }
         String msg = "Audio Settings Permission Denial: " + method + " from pid="
@@ -5404,6 +5391,21 @@
         return false;
     }
 
+    private boolean callingOrSelfHasAudioSettingsPermission() {
+        return mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private boolean callingHasAudioSettingsPermission() {
+        return mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private boolean hasAudioSettingsPermission(int uid, int pid) {
+        return mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     /**
      * Minimum attenuation that can be set for alarms over speaker by an application that
      * doesn't have the MODIFY_AUDIO_SETTINGS permission.
@@ -7082,6 +7084,10 @@
                 case MSG_REINIT_VOLUMES:
                     onReinitVolumes((String) msg.obj);
                     break;
+                case MSG_UPDATE_A11Y_SERVICE_UIDS:
+                    onUpdateAccessibilityServiceUids();
+                    break;
+
             }
         }
     }
@@ -7825,9 +7831,8 @@
 
     @GuardedBy("mHdmiClientLock")
     private void updateHdmiCecSinkLocked(boolean hdmiCecSink) {
-        mHdmiCecSink = hdmiCecSink;
         if (!hasDeviceVolumeBehavior(AudioSystem.DEVICE_OUT_HDMI)) {
-            if (mHdmiCecSink) {
+            if (hdmiCecSink) {
                 if (DEBUG_VOL) {
                     Log.d(TAG, "CEC sink: setting HDMI as full vol device");
                 }
@@ -7885,9 +7890,6 @@
     // Set only when device is a set-top box.
     @GuardedBy("mHdmiClientLock")
     private HdmiPlaybackClient mHdmiPlaybackClient;
-    // true if we are a set-top box, an HDMI sink is connected and it supports CEC.
-    @GuardedBy("mHdmiClientLock")
-    private boolean mHdmiCecSink;
     // Set only when device is an audio system.
     @GuardedBy("mHdmiClientLock")
     private HdmiAudioSystemClient mHdmiAudioSystemClient;
@@ -8142,7 +8144,6 @@
         pw.print("  mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
         pw.print("  mFullVolumeDevices="); pw.println(dumpDeviceTypes(mFullVolumeDevices));
         pw.print("  mExtVolumeController="); pw.println(mExtVolumeController);
-        pw.print("  mHdmiCecSink="); pw.println(mHdmiCecSink);
         pw.print("  mHdmiAudioSystemClient="); pw.println(mHdmiAudioSystemClient);
         pw.print("  mHdmiPlaybackClient="); pw.println(mHdmiPlaybackClient);
         pw.print("  mHdmiTvClient="); pw.println(mHdmiTvClient);
@@ -8153,6 +8154,9 @@
                         + " FromRestrictions=" + mMicMuteFromRestrictions
                         + " FromApi=" + mMicMuteFromApi
                         + " from system=" + mMicMuteFromSystemCached);
+        pw.print("\n  mAssistantUid="); pw.println(mAssistantUid);
+        pw.print("  mCurrentImeUid="); pw.println(mCurrentImeUid);
+        dumpAccessibilityServiceUids(pw);
 
         dumpAudioPolicies(pw);
         mDynPolicyLogger.dump(pw);
@@ -8186,6 +8190,19 @@
         }
     }
 
+    private void dumpAccessibilityServiceUids(PrintWriter pw) {
+        synchronized (mSupportedSystemUsagesLock) {
+            if (mAccessibilityServiceUids != null && mAccessibilityServiceUids.length > 0) {
+                pw.println("  Accessibility service Uids:");
+                for (int uid : mAccessibilityServiceUids) {
+                    pw.println("  - " + uid);
+                }
+            } else {
+                pw.println("  No accessibility service Uids.");
+            }
+        }
+    }
+
     /**
      * Audio Analytics ids.
      */
@@ -8489,7 +8506,8 @@
                         mAccessibilityServiceUids = uids.toArray();
                     }
                 }
-                AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
+                sendMsg(mAudioHandler, MSG_UPDATE_A11Y_SERVICE_UIDS, SENDMSG_REPLACE,
+                        0, 0, null, 0);
             }
         }
 
@@ -8507,6 +8525,14 @@
         }
     }
 
+    private void onUpdateAccessibilityServiceUids() {
+        int[] accessibilityServiceUids;
+        synchronized (mAccessibilityServiceUidsLock) {
+            accessibilityServiceUids = mAccessibilityServiceUids;
+        }
+        AudioSystem.setA11yServicesUids(accessibilityServiceUids);
+    }
+
     //==========================================================================================
     // Audio policy management
     //==========================================================================================
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
new file mode 100644
index 0000000..769c47a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2021 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.biometrics.sensors.face.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.face.AuthenticationFrame;
+import android.hardware.biometrics.face.BaseFrame;
+import android.hardware.biometrics.face.Cell;
+import android.hardware.biometrics.face.EnrollmentFrame;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceDataFrame;
+import android.hardware.face.FaceEnrollCell;
+import android.hardware.face.FaceEnrollFrame;
+
+/**
+ * Utilities for converting between hardware and framework-defined AIDL models.
+ */
+final class AidlConversionUtils {
+    // Prevent instantiation.
+    private AidlConversionUtils() {}
+
+    @NonNull
+    public static FaceAuthenticationFrame convert(@NonNull AuthenticationFrame frame) {
+        return new FaceAuthenticationFrame(convert(frame.data));
+    }
+
+    @NonNull
+    public static AuthenticationFrame convert(@NonNull FaceAuthenticationFrame frame) {
+        final AuthenticationFrame convertedFrame = new AuthenticationFrame();
+        convertedFrame.data = convert(frame.getData());
+        return convertedFrame;
+    }
+
+    @NonNull
+    public static FaceEnrollFrame convert(@NonNull EnrollmentFrame frame) {
+        return new FaceEnrollFrame(convert(frame.cell), frame.stage, convert(frame.data));
+    }
+
+    @NonNull
+    public static EnrollmentFrame convert(@NonNull FaceEnrollFrame frame) {
+        final EnrollmentFrame convertedFrame = new EnrollmentFrame();
+        convertedFrame.cell = convert(frame.getCell());
+        convertedFrame.stage = (byte) frame.getStage();
+        convertedFrame.data = convert(frame.getData());
+        return convertedFrame;
+    }
+
+    @NonNull
+    public static FaceDataFrame convert(@NonNull BaseFrame frame) {
+        return new FaceDataFrame(
+                frame.acquiredInfo,
+                frame.vendorCode,
+                frame.pan,
+                frame.tilt,
+                frame.distance,
+                frame.isCancellable);
+    }
+
+    @NonNull
+    public static BaseFrame convert(@NonNull FaceDataFrame frame) {
+        final BaseFrame convertedFrame = new BaseFrame();
+        convertedFrame.acquiredInfo = (byte) frame.getAcquiredInfo();
+        convertedFrame.vendorCode = frame.getVendorCode();
+        convertedFrame.pan = frame.getPan();
+        convertedFrame.tilt = frame.getTilt();
+        convertedFrame.distance = frame.getDistance();
+        convertedFrame.isCancellable = frame.isCancellable();
+        return convertedFrame;
+    }
+
+    @Nullable
+    public static FaceEnrollCell convert(@Nullable Cell cell) {
+        return cell == null ? null : new FaceEnrollCell(cell.x, cell.y, cell.z);
+    }
+
+    @Nullable
+    public static Cell convert(@Nullable FaceEnrollCell cell) {
+        if (cell == null) {
+            return null;
+        }
+
+        final Cell convertedCell = new Cell();
+        convertedCell.x = cell.getX();
+        convertedCell.y = cell.getY();
+        convertedCell.z = cell.getZ();
+        return convertedCell;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index a7bfc4b..3057766 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -28,6 +28,8 @@
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.biometrics.face.IFace;
 import android.hardware.biometrics.face.ISession;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceDataFrame;
 import android.hardware.face.FaceManager;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -193,6 +195,17 @@
         onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
     }
 
+    /**
+     * Called each time a new frame is received during face authentication.
+     *
+     * @param frame Information about the current frame.
+     */
+    public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame) {
+        // TODO(b/178414967): Send additional frame data to the client callback.
+        final FaceDataFrame data = frame.getData();
+        onAcquired(data.getAcquiredInfo(), data.getVendorCode());
+    }
+
     @Override public void onLockoutTimed(long durationMillis) {
         mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
         // Lockout metrics are logged as an error code.
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index afc7f64..da657b9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -27,6 +27,8 @@
 import android.hardware.biometrics.face.IFace;
 import android.hardware.biometrics.face.ISession;
 import android.hardware.face.Face;
+import android.hardware.face.FaceDataFrame;
+import android.hardware.face.FaceEnrollFrame;
 import android.hardware.face.FaceManager;
 import android.os.IBinder;
 import android.os.NativeHandle;
@@ -110,6 +112,17 @@
         onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
     }
 
+    /**
+     * Called each time a new frame is received during face enrollment.
+     *
+     * @param frame Information about the current frame.
+     */
+    public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) {
+        // TODO(b/178414967): Send additional frame data to the client callback.
+        final FaceDataFrame data = frame.getData();
+        onAcquired(data.getAcquiredInfo(), data.getVendorCode());
+    }
+
     @Override
     protected void startHalOperation() {
         final ArrayList<Byte> token = new ArrayList<>();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index f49601a..640838c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -45,7 +45,6 @@
 import com.android.server.biometrics.SensorStateProto;
 import com.android.server.biometrics.UserStateProto;
 import com.android.server.biometrics.Utils;
-import com.android.server.biometrics.sensors.AcquisitionClient;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -170,33 +169,39 @@
 
         @Override
         public void onAuthenticationFrame(AuthenticationFrame frame) {
-            // TODO(b/174619156): propagate the frame to an AuthenticationClient
             mHandler.post(() -> {
                 final BaseClientMonitor client = mScheduler.getCurrentClient();
-                if (!(client instanceof AcquisitionClient)) {
-                    Slog.e(mTag, "onAcquired for non-acquisition client: "
+                if (!(client instanceof FaceAuthenticationClient)) {
+                    Slog.e(mTag, "onAuthenticationFrame for incompatible client: "
+                            + Utils.getClientName(client));
+                    return;
+
+                }
+                if (frame == null) {
+                    Slog.e(mTag, "Received null authentication frame for client: "
                             + Utils.getClientName(client));
                     return;
                 }
-
-                final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
-                acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
+                ((FaceAuthenticationClient) client).onAuthenticationFrame(
+                        AidlConversionUtils.convert(frame));
             });
         }
 
         @Override
         public void onEnrollmentFrame(EnrollmentFrame frame) {
-            // TODO(b/174619156): propagate the frame to an EnrollmentClient
             mHandler.post(() -> {
                 final BaseClientMonitor client = mScheduler.getCurrentClient();
-                if (!(client instanceof AcquisitionClient)) {
-                    Slog.e(mTag, "onAcquired for non-acquisition client: "
+                if (!(client instanceof FaceEnrollClient)) {
+                    Slog.e(mTag, "onEnrollmentFrame for incompatible client: "
                             + Utils.getClientName(client));
                     return;
                 }
-
-                final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
-                acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
+                if (frame == null) {
+                    Slog.e(mTag, "Received null enrollment frame for client: "
+                            + Utils.getClientName(client));
+                    return;
+                }
+                ((FaceEnrollClient) client).onEnrollmentFrame(AidlConversionUtils.convert(frame));
             });
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 9869f77..1135126 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -113,6 +113,7 @@
     @NonNull private final HalResultController mHalResultController;
     @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
     private int mCurrentUserId = UserHandle.USER_NULL;
+    private boolean mIsUdfps = false;
     private final int mSensorId;
 
     private final class BiometricTaskStackListener extends TaskStackListener {
@@ -335,22 +336,22 @@
         }
 
         final IBiometricsFingerprint daemon = getDaemon();
-        boolean isUdfps = false;
+        mIsUdfps = false;
         android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
                 android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
                         daemon);
         if (extension != null) {
             try {
-                isUdfps = extension.isUdfps(sensorId);
+                mIsUdfps = extension.isUdfps(sensorId);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote exception while quering udfps", e);
-                isUdfps = false;
+                mIsUdfps = false;
             }
         }
 
         final @FingerprintSensorProperties.SensorType int sensorType =
-                isUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
-                        : FingerprintSensorProperties.TYPE_REAR;
+                mIsUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
+                         : FingerprintSensorProperties.TYPE_REAR;
         // resetLockout is controlled by the framework, so hardwareAuthToken is not required
         final boolean resetLockoutRequiresHardwareAuthToken = false;
         final int maxEnrollmentsPerUser = mContext.getResources()
@@ -414,7 +415,7 @@
         Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
                 + mScheduler.getCurrentClient());
         try {
-            mDaemon = IBiometricsFingerprint.getService();
+            mDaemon = IBiometricsFingerprint.getService(true /* retry */);
         } catch (java.util.NoSuchElementException e) {
             // Service doesn't exist or cannot be opened.
             Slog.w(TAG, "NoSuchElementException", e);
@@ -799,6 +800,7 @@
         JSONObject dump = new JSONObject();
         try {
             dump.put("service", TAG);
+            dump.put("isUdfps", mIsUdfps);
 
             JSONArray sets = new JSONArray();
             for (UserInfo user : UserManager.get(mContext).getUsers()) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1a4f20c7..a9a705f 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -210,23 +210,23 @@
     // network is taken down.  This usually only happens to the default network. Lingering ends with
     // either the linger timeout expiring and the network being taken down, or the network
     // satisfying a request again.
-    public static class LingerTimer implements Comparable<LingerTimer> {
+    public static class InactivityTimer implements Comparable<InactivityTimer> {
         public final int requestId;
         public final long expiryMs;
 
-        public LingerTimer(int requestId, long expiryMs) {
+        public InactivityTimer(int requestId, long expiryMs) {
             this.requestId = requestId;
             this.expiryMs = expiryMs;
         }
         public boolean equals(Object o) {
-            if (!(o instanceof LingerTimer)) return false;
-            LingerTimer other = (LingerTimer) o;
+            if (!(o instanceof InactivityTimer)) return false;
+            InactivityTimer other = (InactivityTimer) o;
             return (requestId == other.requestId) && (expiryMs == other.expiryMs);
         }
         public int hashCode() {
             return Objects.hash(requestId, expiryMs);
         }
-        public int compareTo(LingerTimer other) {
+        public int compareTo(InactivityTimer other) {
             return (expiryMs != other.expiryMs) ?
                     Long.compare(expiryMs, other.expiryMs) :
                     Integer.compare(requestId, other.requestId);
@@ -269,30 +269,31 @@
      */
     public static final int ARG_AGENT_SUCCESS = 1;
 
-    // All linger timers for this network, sorted by expiry time. A linger timer is added whenever
+    // All inactivity timers for this network, sorted by expiry time. A timer is added whenever
     // a request is moved to a network with a better score, regardless of whether the network is or
     // was lingering or not.
     // TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
     // SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
-    private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>();
+    private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>();
 
-    // For fast lookups. Indexes into mLingerTimers by request ID.
-    private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>();
+    // For fast lookups. Indexes into mInactivityTimers by request ID.
+    private final SparseArray<InactivityTimer> mInactivityTimerForRequest = new SparseArray<>();
 
-    // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the
-    // network is lingering or not. Always set to the expiry of the LingerTimer that expires last.
-    // When the timer fires, all linger state is cleared, and if the network has no requests, it is
-    // torn down.
-    private WakeupMessage mLingerMessage;
+    // Inactivity expiry timer. Armed whenever mInactivityTimers is non-empty, regardless of
+    // whether the network is inactive or not. Always set to the expiry of the mInactivityTimers
+    // that expires last. When the timer fires, all inactivity state is cleared, and if the network
+    // has no requests, it is torn down.
+    private WakeupMessage mInactivityMessage;
 
-    // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed.
-    private long mLingerExpiryMs;
+    // Inactivity expiry. Holds the expiry time of the inactivity timer, or 0 if the timer is not
+    // armed.
+    private long mInactivityExpiryMs;
 
-    // Whether the network is lingering or not. Must be maintained separately from the above because
+    // Whether the network is inactive or not. Must be maintained separately from the above because
     // it depends on the state of other networks and requests, which only ConnectivityService knows.
     // (Example: we don't linger a network if it would become the best for a NetworkRequest if it
     // validated).
-    private boolean mLingering;
+    private boolean mInactive;
 
     // This represents the quality of the network with no clear scale.
     private int mScore;
@@ -898,17 +899,17 @@
      * ConnectivityService when the request is moved to another network with a higher score.
      */
     public void lingerRequest(int requestId, long now, long duration) {
-        if (mLingerTimerForRequest.get(requestId) != null) {
+        if (mInactivityTimerForRequest.get(requestId) != null) {
             // Cannot happen. Once a request is lingering on a particular network, we cannot
             // re-linger it unless that network becomes the best for that request again, in which
             // case we should have unlingered it.
             Log.wtf(TAG, toShortString() + ": request " + requestId + " already lingered");
         }
         final long expiryMs = now + duration;
-        LingerTimer timer = new LingerTimer(requestId, expiryMs);
-        if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
-        mLingerTimers.add(timer);
-        mLingerTimerForRequest.put(requestId, timer);
+        InactivityTimer timer = new InactivityTimer(requestId, expiryMs);
+        if (VDBG) Log.d(TAG, "Adding InactivityTimer " + timer + " to " + toShortString());
+        mInactivityTimers.add(timer);
+        mInactivityTimerForRequest.put(requestId, timer);
     }
 
     /**
@@ -916,23 +917,25 @@
      * Returns true if the given requestId was lingering on this network, false otherwise.
      */
     public boolean unlingerRequest(int requestId) {
-        LingerTimer timer = mLingerTimerForRequest.get(requestId);
+        InactivityTimer timer = mInactivityTimerForRequest.get(requestId);
         if (timer != null) {
-            if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
-            mLingerTimers.remove(timer);
-            mLingerTimerForRequest.remove(requestId);
+            if (VDBG) {
+                Log.d(TAG, "Removing InactivityTimer " + timer + " from " + toShortString());
+            }
+            mInactivityTimers.remove(timer);
+            mInactivityTimerForRequest.remove(requestId);
             return true;
         }
         return false;
     }
 
-    public long getLingerExpiry() {
-        return mLingerExpiryMs;
+    public long getInactivityExpiry() {
+        return mInactivityExpiryMs;
     }
 
-    public void updateLingerTimer() {
-        long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
-        if (newExpiry == mLingerExpiryMs) return;
+    public void updateInactivityTimer() {
+        long newExpiry = mInactivityTimers.isEmpty() ? 0 : mInactivityTimers.last().expiryMs;
+        if (newExpiry == mInactivityExpiryMs) return;
 
         // Even if we're going to reschedule the timer, cancel it first. This is because the
         // semantics of WakeupMessage guarantee that if cancel is called then the alarm will
@@ -940,49 +943,52 @@
         // WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
         // has already been dispatched, rescheduling to some time in the future won't stop it
         // from calling its callback immediately.
-        if (mLingerMessage != null) {
-            mLingerMessage.cancel();
-            mLingerMessage = null;
+        if (mInactivityMessage != null) {
+            mInactivityMessage.cancel();
+            mInactivityMessage = null;
         }
 
         if (newExpiry > 0) {
-            mLingerMessage = new WakeupMessage(
+            mInactivityMessage = new WakeupMessage(
                     mContext, mHandler,
                     "NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */,
                     EVENT_NETWORK_LINGER_COMPLETE /* cmd */,
                     0 /* arg1 (unused) */, 0 /* arg2 (unused) */,
                     this /* obj (NetworkAgentInfo) */);
-            mLingerMessage.schedule(newExpiry);
+            mInactivityMessage.schedule(newExpiry);
         }
 
-        mLingerExpiryMs = newExpiry;
+        mInactivityExpiryMs = newExpiry;
     }
 
-    public void linger() {
-        mLingering = true;
+    public void setInactive() {
+        mInactive = true;
     }
 
-    public void unlinger() {
-        mLingering = false;
+    public void unsetInactive() {
+        mInactive = false;
     }
 
     public boolean isLingering() {
-        return mLingering;
+        return mInactive;
     }
 
-    public void clearLingerState() {
-        if (mLingerMessage != null) {
-            mLingerMessage.cancel();
-            mLingerMessage = null;
+    public void clearInactivityState() {
+        if (mInactivityMessage != null) {
+            mInactivityMessage.cancel();
+            mInactivityMessage = null;
         }
-        mLingerTimers.clear();
-        mLingerTimerForRequest.clear();
-        updateLingerTimer();  // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage.
-        mLingering = false;
+        mInactivityTimers.clear();
+        mInactivityTimerForRequest.clear();
+        // Sets mInactivityExpiryMs, cancels and nulls out mInactivityMessage.
+        updateInactivityTimer();
+        mInactive = false;
     }
 
-    public void dumpLingerTimers(PrintWriter pw) {
-        for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
+    public void dumpInactivityTimers(PrintWriter pw) {
+        for (InactivityTimer timer : mInactivityTimers) {
+            pw.println(timer);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index d956ba3..e8062ae 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -203,6 +203,7 @@
     protected final NetworkCapabilities mNetworkCapabilities;
     private final SystemServices mSystemServices;
     private final Ikev2SessionCreator mIkev2SessionCreator;
+    private final UserManager mUserManager;
 
     /**
      * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -277,6 +278,10 @@
             return LocalServices.getService(DeviceIdleInternal.class);
         }
 
+        public PendingIntent getIntentForStatusPanel(Context context) {
+            return VpnConfig.getIntentForStatusPanel(context);
+        }
+
         public void sendArgumentsToDaemon(
                 final String daemon, final LocalSocket socket, final String[] arguments,
                 final RetryScheduler retryScheduler) throws IOException, InterruptedException {
@@ -405,6 +410,7 @@
         mLooper = looper;
         mSystemServices = systemServices;
         mIkev2SessionCreator = ikev2SessionCreator;
+        mUserManager = mContext.getSystemService(UserManager.class);
 
         mPackage = VpnConfig.LEGACY_VPN;
         mOwnerUID = getAppUid(mPackage, mUserId);
@@ -1431,7 +1437,7 @@
             final long token = Binder.clearCallingIdentity();
             List<UserInfo> users;
             try {
-                users = UserManager.get(mContext).getAliveUsers();
+                users = mUserManager.getAliveUsers();
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1515,7 +1521,7 @@
      */
     public void onUserAdded(int userId) {
         // If the user is restricted tie them to the parent user's VPN
-        UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+        UserInfo user = mUserManager.getUserInfo(userId);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
                 final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1543,7 +1549,7 @@
      */
     public void onUserRemoved(int userId) {
         // clean up if restricted
-        UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+        UserInfo user = mUserManager.getUserInfo(userId);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
                 final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1768,7 +1774,7 @@
     private void prepareStatusIntent() {
         final long token = Binder.clearCallingIdentity();
         try {
-            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
+            mStatusIntent = mDeps.getIntentForStatusPanel(mContext);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -1968,8 +1974,7 @@
 
     private void enforceNotRestrictedUser() {
         Binder.withCleanCallingIdentity(() -> {
-            final UserManager mgr = UserManager.get(mContext);
-            final UserInfo user = mgr.getUserInfo(mUserId);
+            final UserInfo user = mUserManager.getUserInfo(mUserId);
 
             if (user.isRestricted()) {
                 throw new SecurityException("Restricted users cannot configure VPNs");
@@ -2004,9 +2009,8 @@
      */
     public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
             @Nullable Network underlying, @NonNull LinkProperties egress) {
-        UserManager mgr = UserManager.get(mContext);
-        UserInfo user = mgr.getUserInfo(mUserId);
-        if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+        UserInfo user = mUserManager.getUserInfo(mUserId);
+        if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
                     new UserHandle(mUserId))) {
             throw new SecurityException("Restricted users cannot establish VPNs");
         }
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
new file mode 100644
index 0000000..802472f
--- /dev/null
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 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.devicestate;
+
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * A state of the device defined by the {@link DeviceStateProvider} and managed by the
+ * {@link DeviceStateManagerService}.
+ * <p>
+ * Device state is an abstract concept that allows mapping the current state of the device to the
+ * state of the system. This is useful for variable-state devices, like foldable or rollable
+ * devices, that can be configured by users into differing hardware states, which each may have a
+ * different expected use case.
+ *
+ * @see DeviceStateProvider
+ * @see DeviceStateManagerService
+ */
+public final class DeviceState {
+    /** Unique identifier for the device state. */
+    @IntRange(from = INVALID_DEVICE_STATE)
+    private final int mIdentifier;
+
+    /** String description of the device state. */
+    @NonNull
+    private final String mName;
+
+    public DeviceState(@IntRange(from = INVALID_DEVICE_STATE) int identifier,
+            @NonNull String name) {
+        if (identifier != INVALID_DEVICE_STATE && identifier < 0) {
+            throw new IllegalArgumentException("Identifier must be greater than or equal to zero.");
+        }
+        mIdentifier = identifier;
+        mName = name;
+    }
+
+    /** Returns the unique identifier for the device state. */
+    @IntRange(from = INVALID_DEVICE_STATE)
+    public int getIdentifier() {
+        return mIdentifier;
+    }
+
+    /** Returns a string description of the device state. */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    @Override
+    public String toString() {
+        return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\'' + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DeviceState that = (DeviceState) o;
+        return mIdentifier == that.mIdentifier
+                && Objects.equals(mName, that.mName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIdentifier, mName);
+    }
+}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index d7dcbde..375ec3a 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,7 +19,9 @@
 import static android.Manifest.permission.CONTROL_DEVICE_STATE;
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.devicestate.IDeviceStateManager;
@@ -29,7 +31,6 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
-import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -42,7 +43,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Optional;
 
 /**
  * A system service that manages the state of a device with user-configurable hardware like a
@@ -74,26 +75,31 @@
     @NonNull
     private final BinderService mBinderService;
 
+    // All supported device states keyed by identifier.
     @GuardedBy("mLock")
-    private IntArray mSupportedDeviceStates;
+    private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
 
-    // The current committed device state.
+    // The current committed device state. The default of INVALID_DEVICE_STATE will be replaced by
+    // the current state after the initial callback from the DeviceStateProvider.
     @GuardedBy("mLock")
-    private int mCommittedState = INVALID_DEVICE_STATE;
+    @NonNull
+    private DeviceState mCommittedState = new DeviceState(INVALID_DEVICE_STATE, "INVALID");
     // The device state that is currently awaiting callback from the policy to be committed.
     @GuardedBy("mLock")
-    private int mPendingState = INVALID_DEVICE_STATE;
+    @NonNull
+    private Optional<DeviceState> mPendingState = Optional.empty();
     // Whether or not the policy is currently waiting to be notified of the current pending state.
     @GuardedBy("mLock")
     private boolean mIsPolicyWaitingForState = false;
     // The device state that is currently requested and is next to be configured and committed.
     // Can be overwritten by an override state value if requested.
     @GuardedBy("mLock")
-    private int mRequestedState = INVALID_DEVICE_STATE;
-    // The most recently requested override state, or INVALID_DEVICE_STATE if no override is
-    // requested.
+    @NonNull
+    private Optional<DeviceState> mRequestedState = Optional.empty();
+    // The most recently requested override state, or empty if no override is requested.
     @GuardedBy("mLock")
-    private int mRequestedOverrideState = INVALID_DEVICE_STATE;
+    @NonNull
+    private Optional<DeviceState> mRequestedOverrideState = Optional.empty();
 
     // List of registered callbacks indexed by process id.
     @GuardedBy("mLock")
@@ -122,18 +128,20 @@
      *
      * @see #getPendingState()
      */
-    int getCommittedState() {
+    @NonNull
+    DeviceState getCommittedState() {
         synchronized (mLock) {
             return mCommittedState;
         }
     }
 
     /**
-     * Returns the state the system is currently configuring, or {@link #INVALID_DEVICE_STATE} if
-     * the system is not in the process of configuring a state.
+     * Returns the state the system is currently configuring, or {@link Optional#empty()} if the
+     * system is not in the process of configuring a state.
      */
     @VisibleForTesting
-    int getPendingState() {
+    @NonNull
+    Optional<DeviceState> getPendingState() {
         synchronized (mLock) {
             return mPendingState;
         }
@@ -143,7 +151,8 @@
      * Returns the requested state. The service will configure the device to match the requested
      * state when possible.
      */
-    int getRequestedState() {
+    @NonNull
+    Optional<DeviceState> getRequestedState() {
         synchronized (mLock) {
             return mRequestedState;
         }
@@ -165,7 +174,7 @@
                 return false;
             }
 
-            mRequestedOverrideState = overrideState;
+            mRequestedOverrideState = getStateLocked(overrideState);
             updatePendingStateLocked();
         }
 
@@ -181,20 +190,24 @@
     }
 
     /**
-     * Returns the current requested override state, or {@link #INVALID_DEVICE_STATE} is no override
+     * Returns the current requested override state, or {@link Optional#empty()} if no override
      * state is requested.
      */
-    int getOverrideState() {
+    @NonNull
+    Optional<DeviceState> getOverrideState() {
         synchronized (mLock) {
             return mRequestedOverrideState;
         }
     }
 
     /** Returns the list of currently supported device states. */
-    int[] getSupportedStates() {
+    DeviceState[] getSupportedStates() {
         synchronized (mLock) {
-            // Copy array to prevent external modification of internal state.
-            return Arrays.copyOf(mSupportedDeviceStates.toArray(), mSupportedDeviceStates.size());
+            DeviceState[] supportedStates = new DeviceState[mDeviceStates.size()];
+            for (int i = 0; i < supportedStates.length; i++) {
+                supportedStates[i] = mDeviceStates.valueAt(i);
+            }
+            return supportedStates;
         }
     }
 
@@ -203,24 +216,26 @@
         return mBinderService;
     }
 
-    private void updateSupportedStates(int[] supportedDeviceStates) {
-        // Must ensure sorted as isSupportedStateLocked() impl uses binary search.
-        Arrays.sort(supportedDeviceStates, 0, supportedDeviceStates.length);
+    private void updateSupportedStates(DeviceState[] supportedDeviceStates) {
         synchronized (mLock) {
-            mSupportedDeviceStates = IntArray.wrap(supportedDeviceStates);
+            mDeviceStates.clear();
+            for (int i = 0; i < supportedDeviceStates.length; i++) {
+                DeviceState state = supportedDeviceStates[i];
+                mDeviceStates.put(state.getIdentifier(), state);
+            }
 
-            if (mRequestedState != INVALID_DEVICE_STATE
-                    && !isSupportedStateLocked(mRequestedState)) {
+            if (mRequestedState.isPresent()
+                    && !isSupportedStateLocked(mRequestedState.get().getIdentifier())) {
                 // The current requested state is no longer valid. We'll clear it here, though
                 // we won't actually update the current state until a callback comes from the
                 // provider with the most recent state.
-                mRequestedState = INVALID_DEVICE_STATE;
+                mRequestedState = Optional.empty();
             }
-            if (mRequestedOverrideState != INVALID_DEVICE_STATE
-                    && !isSupportedStateLocked(mRequestedOverrideState)) {
+            if (mRequestedOverrideState.isPresent()
+                    && !isSupportedStateLocked(mRequestedOverrideState.get().getIdentifier())) {
                 // The current override state is no longer valid. We'll clear it here and update
                 // the committed state if necessary.
-                mRequestedOverrideState = INVALID_DEVICE_STATE;
+                mRequestedOverrideState = Optional.empty();
             }
             updatePendingStateLocked();
         }
@@ -230,10 +245,19 @@
 
     /**
      * Returns {@code true} if the provided state is supported. Requires that
-     * {@link #mSupportedDeviceStates} is sorted prior to calling.
+     * {@link #mDeviceStates} is sorted prior to calling.
      */
-    private boolean isSupportedStateLocked(int state) {
-        return mSupportedDeviceStates.binarySearch(state) >= 0;
+    private boolean isSupportedStateLocked(int identifier) {
+        return mDeviceStates.contains(identifier);
+    }
+
+    /**
+     * Returns the {@link DeviceState} with the supplied {@code identifier}, or {@code null} if
+     * there is no device state with the identifier.
+     */
+    @Nullable
+    private Optional<DeviceState> getStateLocked(int identifier) {
+        return Optional.ofNullable(mDeviceStates.get(identifier));
     }
 
     /**
@@ -242,10 +266,11 @@
      *
      * @see #isSupportedStateLocked(int)
      */
-    private void requestState(int state) {
+    private void requestState(int identifier) {
         synchronized (mLock) {
-            if (isSupportedStateLocked(state)) {
-                mRequestedState = state;
+            final Optional<DeviceState> requestedState = getStateLocked(identifier);
+            if (requestedState.isPresent()) {
+                mRequestedState = requestedState;
             }
             updatePendingStateLocked();
         }
@@ -259,19 +284,19 @@
      * changed.
      */
     private void updatePendingStateLocked() {
-        if (mPendingState != INVALID_DEVICE_STATE) {
+        if (mPendingState.isPresent()) {
             // Have pending state, can not configure a new state until the state is committed.
             return;
         }
 
-        int stateToConfigure;
-        if (mRequestedOverrideState != INVALID_DEVICE_STATE) {
-            stateToConfigure = mRequestedOverrideState;
+        final DeviceState stateToConfigure;
+        if (mRequestedOverrideState.isPresent()) {
+            stateToConfigure = mRequestedOverrideState.get();
         } else {
-            stateToConfigure = mRequestedState;
+            stateToConfigure = mRequestedState.orElse(null);
         }
 
-        if (stateToConfigure == INVALID_DEVICE_STATE) {
+        if (stateToConfigure == null) {
             // No currently requested state.
             return;
         }
@@ -281,7 +306,7 @@
             return;
         }
 
-        mPendingState = stateToConfigure;
+        mPendingState = Optional.of(stateToConfigure);
         mIsPolicyWaitingForState = true;
     }
 
@@ -302,7 +327,7 @@
                 return;
             }
             mIsPolicyWaitingForState = false;
-            state = mPendingState;
+            state = mPendingState.get().getIdentifier();
         }
 
         if (DEBUG) {
@@ -333,9 +358,9 @@
             if (DEBUG) {
                 Slog.d(TAG, "Committing state: " + mPendingState);
             }
-            mCommittedState = mPendingState;
-            newState = mCommittedState;
-            mPendingState = INVALID_DEVICE_STATE;
+            mCommittedState = mPendingState.get();
+            newState = mCommittedState.getIdentifier();
+            mPendingState = Optional.empty();
             updatePendingStateLocked();
         }
 
@@ -389,7 +414,7 @@
             }
 
             mCallbacks.put(callingPid, record);
-            currentState = mCommittedState;
+            currentState = mCommittedState.getIdentifier();
         }
 
         // Notify the callback of the state at registration.
@@ -406,10 +431,10 @@
         pw.println("DEVICE STATE MANAGER (dumpsys device_state)");
 
         synchronized (mLock) {
-            pw.println("  mCommittedState=" + toString(mCommittedState));
-            pw.println("  mPendingState=" + toString(mPendingState));
-            pw.println("  mRequestedState=" + toString(mRequestedState));
-            pw.println("  mRequestedOverrideState=" + toString(mRequestedOverrideState));
+            pw.println("  mCommittedState=" + mCommittedState);
+            pw.println("  mPendingState=" + mPendingState);
+            pw.println("  mRequestedState=" + mRequestedState);
+            pw.println("  mRequestedOverrideState=" + mRequestedOverrideState);
 
             final int callbackCount = mCallbacks.size();
             pw.println();
@@ -421,30 +446,28 @@
         }
     }
 
-    private String toString(int state) {
-        return state == INVALID_DEVICE_STATE ? "(none)" : String.valueOf(state);
-    }
-
     private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
         @Override
-        public void onSupportedDeviceStatesChanged(int[] newDeviceStates) {
+        public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
+            if (newDeviceStates.length == 0) {
+                throw new IllegalArgumentException("Supported device states must not be empty");
+            }
             for (int i = 0; i < newDeviceStates.length; i++) {
-                if (newDeviceStates[i] < 0) {
-                    throw new IllegalArgumentException("Supported device states includes invalid"
-                            + " value: " + newDeviceStates[i]);
+                if (newDeviceStates[i].getIdentifier() == INVALID_DEVICE_STATE) {
+                    throw new IllegalArgumentException(
+                            "Supported device states includes INVALID_DEVICE_STATE identifier");
                 }
             }
-
             updateSupportedStates(newDeviceStates);
         }
 
         @Override
-        public void onStateChanged(int state) {
-            if (state < 0) {
-                throw new IllegalArgumentException("Invalid state: " + state);
+        public void onStateChanged(@IntRange(from = 0) int identifier) {
+            if (identifier < 0) {
+                throw new IllegalArgumentException("Invalid identifier: " + identifier);
             }
 
-            requestState(state);
+            requestState(identifier);
         }
     }
 
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index cf3b297..7914531 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -16,11 +16,10 @@
 
 package com.android.server.devicestate;
 
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-
 import android.os.ShellCommand;
 
 import java.io.PrintWriter;
+import java.util.Optional;
 
 /**
  * ShellCommands for {@link DeviceStateManagerService}.
@@ -52,24 +51,15 @@
     }
 
     private void printState(PrintWriter pw) {
-        int committedState = mInternal.getCommittedState();
-        int requestedState = mInternal.getRequestedState();
-        int requestedOverrideState = mInternal.getOverrideState();
+        DeviceState committedState = mInternal.getCommittedState();
+        Optional<DeviceState> requestedState = mInternal.getRequestedState();
+        Optional<DeviceState> requestedOverrideState = mInternal.getOverrideState();
 
-        if (committedState == INVALID_DEVICE_STATE) {
-            pw.println("Device state: (invalid)");
-        } else {
-            pw.println("Device state: " + committedState);
-        }
-
-        if (requestedOverrideState != INVALID_DEVICE_STATE) {
+        pw.println("Committed state: " + committedState);
+        if (requestedOverrideState.isPresent()) {
             pw.println("----------------------");
-            if (requestedState == INVALID_DEVICE_STATE) {
-                pw.println("Base state: (invalid)");
-            } else {
-                pw.println("Base state: " + requestedState);
-            }
-            pw.println("Override state: " + committedState);
+            pw.println("Base state: " + requestedState.orElse(null));
+            pw.println("Override state: " + requestedOverrideState.get());
         }
     }
 
@@ -102,15 +92,12 @@
     }
 
     private int runPrintStates(PrintWriter pw) {
-        int[] states = mInternal.getSupportedStates();
-        pw.print("Supported states: [ ");
+        DeviceState[] states = mInternal.getSupportedStates();
+        pw.print("Supported states: [\n");
         for (int i = 0; i < states.length; i++) {
-            pw.print(states[i]);
-            if (i < states.length - 1) {
-                pw.print(", ");
-            }
+            pw.print("  " + states[i] + ",\n");
         }
-        pw.println(" ]");
+        pw.println("]");
         return 0;
     }
 
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index 0e8bf9b..2d4377f 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -16,9 +16,11 @@
 
 package com.android.server.devicestate;
 
+import android.annotation.IntRange;
+
 /**
- * Responsible for providing the set of currently supported device states and well as the current
- * device state.
+ * Responsible for providing the set of supported {@link DeviceState device states} as well as the
+ * current device state.
  *
  * @see DeviceStatePolicy
  */
@@ -26,8 +28,8 @@
     /**
      * Registers a listener for changes in provider state.
      * <p>
-     * It is <b>required</b> that {@link Listener#onSupportedDeviceStatesChanged(int[])} be called
-     * followed by {@link Listener#onStateChanged(int)} with the initial values on successful
+     * It is <b>required</b> that {@link Listener#onSupportedDeviceStatesChanged(DeviceState[])} be
+     * called followed by {@link Listener#onStateChanged(int)} with the initial values on successful
      * registration of the listener.
      */
     void setListener(Listener listener);
@@ -35,35 +37,36 @@
     /** Callback for changes in {@link DeviceStateProvider} state. */
     interface Listener {
         /**
-         * Called to notify the listener of a change in supported device states. Required to be
-         * called once on successful registration of the listener and then once on every
-         * subsequent change in supported device states.
+         * Called to notify the listener of a change in supported {@link DeviceState device states}.
+         * Required to be called once on successful registration of the listener and then once on
+         * every subsequent change in supported device states.
          * <p>
          * The set of device states can change based on the current hardware state of the device.
          * For example, if a device state depends on a particular peripheral device (display, etc)
          * it would only be reported as supported when the device is plugged. Otherwise, it should
          * not be included in the set of supported states.
          * <p>
-         * All values provided must be greater than or equal to zero and there must always be at
-         * least one supported device state.
+         * The identifier for every provided device state must be unique and greater than or equal
+         * to zero and there must always be at least one supported device state.
          *
          * @param newDeviceStates array of supported device states.
          *
          * @throws IllegalArgumentException if the list of device states is empty or if one of the
-         * provided states is less than 0.
+         * provided states contains an invalid identifier.
          */
-        void onSupportedDeviceStatesChanged(int[] newDeviceStates);
+        void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates);
 
         /**
          * Called to notify the listener of a change in current device state. Required to be called
          * once on successful registration of the listener and then once on every subsequent change
          * in device state. Value must have been included in the set of supported device states
-         * provided in the most recent call to {@link #onSupportedDeviceStatesChanged(int[])}.
+         * provided in the most recent call to
+         * {@link #onSupportedDeviceStatesChanged(DeviceState[])}.
          *
-         * @param state the new device state.
+         * @param identifier the identifier of the new device state.
          *
          * @throws IllegalArgumentException if the state is less than 0.
          */
-        void onStateChanged(int state);
+        void onStateChanged(@IntRange(from = 0) int identifier);
     }
 }
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index b8e579d..4832e46 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -300,6 +300,8 @@
     /** @return The default brightness configuration. */
     public abstract BrightnessConfiguration getDefaultConfig();
 
+    /** Recalculates the backlight-to-nits and nits-to-backlight splines. */
+    public abstract void recalculateSplines(boolean applyAdjustment, float[] adjustment);
 
     /**
      * Returns the timeout for the short term model
@@ -658,6 +660,11 @@
         }
 
         @Override
+        public void recalculateSplines(boolean applyAdjustment, float[] adjustment) {
+            // Do nothing.
+        }
+
+        @Override
         public void dump(PrintWriter pw) {
             pw.println("SimpleMappingStrategy");
             pw.println("  mSpline=" + mSpline);
@@ -696,7 +703,7 @@
 
         // A spline mapping from nits to the corresponding backlight value, normalized to the range
         // [0, 1.0].
-        private final Spline mNitsToBacklightSpline;
+        private Spline mNitsToBacklightSpline;
 
         // The default brightness configuration.
         private final BrightnessConfiguration mDefaultConfig;
@@ -705,6 +712,11 @@
         // a brightness in nits.
         private Spline mBacklightToNitsSpline;
 
+        private float[] mNits;
+        private int[] mBacklight;
+
+        private boolean mBrightnessRangeAdjustmentApplied;
+
         private float mMaxGamma;
         private float mAutoBrightnessAdjustment;
         private float mUserLux;
@@ -726,15 +738,9 @@
             mUserLux = -1;
             mUserBrightness = -1;
 
-            // Setup the backlight spline
-            final int N = nits.length;
-            float[] normalizedBacklight = new float[N];
-            for (int i = 0; i < N; i++) {
-                normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);
-            }
-
-            mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);
-            mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
+            mNits = nits;
+            mBacklight = backlight;
+            computeNitsBrightnessSplines(mNits);
 
             mDefaultConfig = config;
             if (mLoggingEnabled) {
@@ -868,6 +874,12 @@
         }
 
         @Override
+        public void recalculateSplines(boolean applyAdjustment, float[] adjustedNits) {
+            mBrightnessRangeAdjustmentApplied = applyAdjustment;
+            computeNitsBrightnessSplines(mBrightnessRangeAdjustmentApplied ? adjustedNits : mNits);
+        }
+
+        @Override
         public void dump(PrintWriter pw) {
             pw.println("PhysicalMappingStrategy");
             pw.println("  mConfig=" + mConfig);
@@ -878,6 +890,18 @@
             pw.println("  mUserLux=" + mUserLux);
             pw.println("  mUserBrightness=" + mUserBrightness);
             pw.println("  mDefaultConfig=" + mDefaultConfig);
+            pw.println("  mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied);
+        }
+
+        private void computeNitsBrightnessSplines(float[] nits) {
+            final int len = nits.length;
+            float[] normalizedBacklight = new float[len];
+            for (int i = 0; i < len; i++) {
+                normalizedBacklight[i] = normalizeAbsoluteBrightness(mBacklight[i]);
+            }
+
+            mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);
+            mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
         }
 
         private void computeSpline() {
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 2375f74..a186e33 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -63,7 +63,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.RingBuffer;
 import com.android.server.LocalServices;
 
@@ -71,7 +70,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -80,7 +78,6 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -118,6 +115,9 @@
     private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
     private static final String ATTR_NIGHT_MODE = "nightMode";
     private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
+    private static final String ATTR_REDUCE_BRIGHT_COLORS = "reduceBrightColors";
+    private static final String ATTR_REDUCE_BRIGHT_COLORS_STRENGTH = "reduceBrightColorsStrength";
+    private static final String ATTR_REDUCE_BRIGHT_COLORS_OFFSET = "reduceBrightColorsOffset";
     private static final String ATTR_LAST_NITS = "lastNits";
     private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
     private static final String ATTR_POWER_SAVE = "powerSaveFactor";
@@ -398,6 +398,10 @@
 
         builder.setNightMode(mInjector.isNightDisplayActivated(mContext));
         builder.setColorTemperature(mInjector.getNightDisplayColorTemperature(mContext));
+        builder.setReduceBrightColors(mInjector.isReduceBrightColorsActivated(mContext));
+        builder.setReduceBrightColorsStrength(mInjector.getReduceBrightColorsStrength(mContext));
+        builder.setReduceBrightColorsOffset(mInjector.getReduceBrightColorsOffsetFactor(mContext)
+                * brightness);
 
         if (mColorSamplingEnabled) {
             DisplayedContentSample sample = mInjector.sampleColor(mNoFramesToSample);
@@ -563,6 +567,12 @@
                 out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode);
                 out.attributeInt(null, ATTR_COLOR_TEMPERATURE,
                         toWrite[i].colorTemperature);
+                out.attributeBoolean(null, ATTR_REDUCE_BRIGHT_COLORS,
+                        toWrite[i].reduceBrightColors);
+                out.attributeInt(null, ATTR_REDUCE_BRIGHT_COLORS_STRENGTH,
+                        toWrite[i].reduceBrightColorsStrength);
+                out.attributeFloat(null, ATTR_REDUCE_BRIGHT_COLORS_OFFSET,
+                        toWrite[i].reduceBrightColorsOffset);
                 out.attributeFloat(null, ATTR_LAST_NITS,
                         toWrite[i].lastBrightness);
                 out.attributeBoolean(null, ATTR_DEFAULT_CONFIG,
@@ -641,6 +651,12 @@
                     builder.setNightMode(parser.getAttributeBoolean(null, ATTR_NIGHT_MODE));
                     builder.setColorTemperature(
                             parser.getAttributeInt(null, ATTR_COLOR_TEMPERATURE));
+                    builder.setReduceBrightColors(
+                            parser.getAttributeBoolean(null, ATTR_REDUCE_BRIGHT_COLORS));
+                    builder.setReduceBrightColorsStrength(
+                            parser.getAttributeInt(null, ATTR_REDUCE_BRIGHT_COLORS_STRENGTH));
+                    builder.setReduceBrightColorsOffset(
+                            parser.getAttributeFloat(null, ATTR_REDUCE_BRIGHT_COLORS_OFFSET));
                     builder.setLastBrightness(parser.getAttributeFloat(null, ATTR_LAST_NITS));
 
                     String luxValue = parser.getAttributeValue(null, ATTR_LUX);
@@ -1114,6 +1130,21 @@
             return context.getSystemService(ColorDisplayManager.class).isNightDisplayActivated();
         }
 
+        public int getReduceBrightColorsStrength(Context context) {
+            return context.getSystemService(ColorDisplayManager.class)
+                    .getReduceBrightColorsStrength();
+        }
+
+        public float getReduceBrightColorsOffsetFactor(Context context) {
+            return context.getSystemService(ColorDisplayManager.class)
+                    .getReduceBrightColorsOffsetFactor();
+        }
+
+        public boolean isReduceBrightColorsActivated(Context context) {
+            return context.getSystemService(ColorDisplayManager.class)
+                    .isReduceBrightColorsActivated();
+        }
+
         public DisplayedContentSample sampleColor(int noFramesToSample) {
             final DisplayManagerInternal displayManagerInternal =
                     LocalServices.getService(DisplayManagerInternal.class);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e0baee7..e5151d8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -181,8 +181,8 @@
     private static final String FORCE_WIFI_DISPLAY_ENABLE = "persist.debug.wfd.enable";
 
     private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";
-
     private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
+    private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.1f;
 
     private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
     private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;
@@ -371,7 +371,7 @@
 
     private final boolean mAllowNonNativeRefreshRateOverride;
 
-    private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.1f;
+    private final BrightnessSynchronizer mBrightnessSynchronizer;
 
     /**
      * Applications use {@link android.view.Display#getRefreshRate} and
@@ -408,6 +408,7 @@
         mLogicalDisplayMapper = new LogicalDisplayMapper(context, mDisplayDeviceRepo,
                 new LogicalDisplayListener());
         mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
+        mBrightnessSynchronizer = new BrightnessSynchronizer(mContext);
         Resources resources = mContext.getResources();
         mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
@@ -542,6 +543,7 @@
         mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
 
         mSettingsObserver = new SettingsObserver();
+        mBrightnessSynchronizer.startSynchronizing();
     }
 
     @VisibleForTesting
@@ -2761,22 +2763,8 @@
         public boolean requestPowerState(int groupId, DisplayPowerRequest request,
                 boolean waitForNegativeProximity) {
             synchronized (mSyncRoot) {
-                final DisplayGroup displayGroup = mLogicalDisplayMapper.getDisplayGroupLocked(
-                        groupId);
-                if (displayGroup == null) {
-                    return true;
-                }
-
-                final int size = displayGroup.getSizeLocked();
-                boolean ready = true;
-                for (int i = 0; i < size; i++) {
-                    final DisplayPowerController displayPowerController =
-                            mDisplayPowerControllers.get(displayGroup.getIdLocked(i));
-                    ready &= displayPowerController.requestPowerState(request,
-                            waitForNegativeProximity);
-                }
-
-                return ready;
+                return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
+                        .requestPowerState(request, waitForNegativeProximity);
             }
         }
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 13dc0b9..2df33652 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -47,6 +47,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.TimeUtils;
@@ -58,6 +59,8 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
+import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
+import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings;
@@ -152,6 +155,7 @@
     private final DisplayPowerCallbacks mCallbacks;
 
     // Battery stats.
+    @Nullable
     private final IBatteryStats mBatteryStats;
 
     // The sensor manager.
@@ -170,6 +174,7 @@
     private final int mDisplayId;
 
     // Tracker for brightness changes.
+    @Nullable
     private final BrightnessTracker mBrightnessTracker;
 
     // Tracker for brightness settings changes.
@@ -346,6 +351,9 @@
     @Nullable
     private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
 
+    private final ColorDisplayServiceInternal mCdsi;
+    private final float[] mNitsRange;
+
     // A record of state for skipping brightness ramps.
     private int mSkipRampState = RAMP_STATE_SKIP_NONE;
 
@@ -401,30 +409,30 @@
     private ObjectAnimator mColorFadeOffAnimator;
     private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
 
-    // The brightness synchronizer to allow changes in the int brightness value to be reflected in
-    // the float brightness value and vice versa.
-    @Nullable
-    private final BrightnessSynchronizer mBrightnessSynchronizer;
-
     /**
      * Creates the display power controller.
      */
     public DisplayPowerController(Context context,
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay) {
+        mLogicalDisplay = logicalDisplay;
+        mDisplayId = mLogicalDisplay.getDisplayIdLocked();
         mHandler = new DisplayControllerHandler(handler.getLooper());
-        mBrightnessTracker = new BrightnessTracker(context, null);
+
+        if (mDisplayId == Display.DEFAULT_DISPLAY) {
+            mBrightnessTracker = new BrightnessTracker(context, null);
+            mBatteryStats = BatteryStatsService.getService();
+        } else {
+            mBrightnessTracker = null;
+            mBatteryStats = null;
+        }
+
         mSettingsObserver = new SettingsObserver(mHandler);
         mCallbacks = callbacks;
-        mBatteryStats = BatteryStatsService.getService();
         mSensorManager = sensorManager;
         mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
         mBlanker = blanker;
         mContext = context;
-        mBrightnessSynchronizer = new BrightnessSynchronizer(context);
-        mBrightnessSynchronizer.startSynchronizing();
-        mLogicalDisplay = logicalDisplay;
-        mDisplayId = mLogicalDisplay.getDisplayIdLocked();
 
         PowerManager pm = context.getSystemService(PowerManager.class);
 
@@ -455,8 +463,12 @@
         mScreenBrightnessForVrRangeMinimum = clampAbsoluteBrightness(
                 pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR));
 
+        // Check the setting, but also verify that it is the default display. Only the default
+        // display has an automatic brightness controller running.
+        // TODO: b/179021925 - Fix to work with multiple displays
         mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
-                com.android.internal.R.bool.config_automatic_brightness_available);
+                com.android.internal.R.bool.config_automatic_brightness_available)
+                && mDisplayId == Display.DEFAULT_DISPLAY;
 
         mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
@@ -552,7 +564,9 @@
         mBrightnessBucketsInDozeConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_displayBrightnessBucketsInDoze);
 
-        if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
+        if (mDisplayId == Display.DEFAULT_DISPLAY && !DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
+            // TODO: b/178385123 Once there are sensor associations, we can enable proximity for
+            // non-default displays.
             mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
             if (mProximitySensor != null) {
                 mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
@@ -580,6 +594,42 @@
         }
         mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
         mDisplayWhiteBalanceController = displayWhiteBalanceController;
+
+        if (displayDeviceConfig != null && displayDeviceConfig.getNits() != null) {
+            mNitsRange = displayDeviceConfig.getNits();
+        } else {
+            Slog.w(TAG, "Screen brightness nits configuration is unavailable; falling back");
+            mNitsRange = BrightnessMappingStrategy.getFloatArray(context.getResources()
+                    .obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits));
+        }
+        mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
+        boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
+            @Override
+            public void onReduceBrightColorsActivationChanged(boolean activated) {
+                applyReduceBrightColorsSplineAdjustment();
+            }
+
+            @Override
+            public void onReduceBrightColorsStrengthChanged(int strength) {
+                applyReduceBrightColorsSplineAdjustment();
+            }
+        });
+        if (active) {
+            applyReduceBrightColorsSplineAdjustment();
+        }
+    }
+
+    private void applyReduceBrightColorsSplineAdjustment() {
+        if (mBrightnessMapper == null) {
+            Log.w(TAG, "No brightness mapping available to recalculate splines");
+            return;
+        }
+
+        float[] adjustedNits = new float[mNitsRange.length];
+        for (int i = 0; i < mNitsRange.length; i++) {
+            adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
+        }
+        mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits);
     }
 
     private Sensor findDisplayLightSensor(String sensorType) {
@@ -607,18 +657,28 @@
      * @param userId userId to fetch data for
      * @param includePackage if false will null out the package name in events
      */
+    @Nullable
     public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
             @UserIdInt int userId, boolean includePackage) {
+        if (mBrightnessTracker == null) {
+            return null;
+        }
         return mBrightnessTracker.getEvents(userId, includePackage);
     }
 
     public void onSwitchUser(@UserIdInt int newUserId) {
         handleSettingsChange(true /* userSwitch */);
-        mBrightnessTracker.onSwitchUser(newUserId);
+        if (mBrightnessTracker != null) {
+            mBrightnessTracker.onSwitchUser(newUserId);
+        }
     }
 
+    @Nullable
     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
             @UserIdInt int userId) {
+        if (mBrightnessTracker == null) {
+            return null;
+        }
         return mBrightnessTracker.getAmbientBrightnessStats(userId);
     }
 
@@ -626,7 +686,9 @@
      * Persist the brightness slider events and ambient brightness stats to disk.
      */
     public void persistBrightnessTrackerState() {
-        mBrightnessTracker.persistBrightnessTrackerState();
+        if (mBrightnessTracker != null) {
+            mBrightnessTracker.persistBrightnessTrackerState();
+        }
     }
 
     /**
@@ -730,20 +792,16 @@
                 mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT);
         mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
 
-        // Initialize screen state for battery stats.
-        try {
-            mBatteryStats.noteScreenState(mPowerState.getScreenState());
-            mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(
-                    mPowerState.getScreenBrightness()));
-        } catch (RemoteException ex) {
-            // same process
-        }
+        noteScreenState(mPowerState.getScreenState());
+        noteScreenBrightness(mPowerState.getScreenBrightness());
+
         // Initialize all of the brightness tracking state
         final float brightness = convertToNits(BrightnessSynchronizer.brightnessFloatToInt(
                 mPowerState.getScreenBrightness()));
         if (brightness >= 0.0f) {
             mBrightnessTracker.start(brightness);
         }
+
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT),
                 false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
@@ -1330,11 +1388,7 @@
                 SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
                 mPowerState.setScreenState(state);
                 // Tell battery stats about the transition.
-                try {
-                    mBatteryStats.noteScreenState(state);
-                } catch (RemoteException ex) {
-                    // same process
-                }
+                noteScreenState(state);
             }
         }
 
@@ -1406,13 +1460,7 @@
             Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
             // TODO(b/153319140) remove when we can get this from the above trace invocation
             SystemProperties.set("debug.tracing.screen_brightness", String.valueOf(target));
-            try {
-                // TODO(brightnessfloat): change BatteryStats to use float
-                mBatteryStats.noteScreenBrightness(
-                        BrightnessSynchronizer.brightnessFloatToInt(target));
-            } catch (RemoteException ex) {
-                // same process
-            }
+            noteScreenBrightness(target);
         }
     }
 
@@ -1731,15 +1779,21 @@
     }
 
     private void putScreenBrightnessSetting(float brightnessValue) {
-        mCurrentScreenBrightnessSetting = brightnessValue;
-        Settings.System.putFloatForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessValue, UserHandle.USER_CURRENT);
+        if (mDisplayId == Display.DEFAULT_DISPLAY) {
+            mCurrentScreenBrightnessSetting = brightnessValue;
+            Settings.System.putFloatForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessValue,
+                    UserHandle.USER_CURRENT);
+        }
     }
 
     private void putAutoBrightnessAdjustmentSetting(float adjustment) {
-        mAutoBrightnessAdjustment = adjustment;
-        Settings.System.putFloatForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment, UserHandle.USER_CURRENT);
+        if (mDisplayId == Display.DEFAULT_DISPLAY) {
+            mAutoBrightnessAdjustment = adjustment;
+            Settings.System.putFloatForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment,
+                    UserHandle.USER_CURRENT);
+        }
     }
 
     private boolean updateAutoBrightnessAdjustment() {
@@ -2020,6 +2074,29 @@
         return MathUtils.constrain(value, -1.0f, 1.0f);
     }
 
+    private void noteScreenState(int screenState) {
+        if (mBatteryStats != null) {
+            try {
+                // TODO(multi-display): make this multi-display
+                mBatteryStats.noteScreenState(screenState);
+            } catch (RemoteException e) {
+                // same process
+            }
+        }
+    }
+
+    private void noteScreenBrightness(float brightness) {
+        if (mBatteryStats != null) {
+            try {
+                // TODO(brightnessfloat): change BatteryStats to use float
+                mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(
+                        brightness));
+            } catch (RemoteException e) {
+                // same process
+            }
+        }
+    }
+
     private final class DisplayControllerHandler extends Handler {
         public DisplayControllerHandler(Looper looper) {
             super(looper, null, true /*async*/);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 8ee01be..16c4b26 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -107,7 +107,7 @@
 
     private final DisplayDeviceRepository mDisplayDeviceRepo;
     private final Listener mListener;
-    private final int mFoldedDeviceState;
+    private final int[] mFoldedDeviceStates;
 
     LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener) {
         mDisplayDeviceRepo = repo;
@@ -115,8 +115,8 @@
         mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
         mDisplayDeviceRepo.addListener(this);
 
-        mFoldedDeviceState = context.getResources().getInteger(
-                com.android.internal.R.integer.config_foldedDeviceState);
+        mFoldedDeviceStates = context.getResources().getIntArray(
+                com.android.internal.R.array.config_foldedDeviceStates);
 
         loadFoldedDisplayConfig(context);
     }
@@ -232,7 +232,14 @@
     }
 
     void setDeviceStateLocked(int state) {
-        setDeviceFoldedLocked(state == mFoldedDeviceState);
+        boolean folded = false;
+        for (int i = 0; i < mFoldedDeviceStates.length; i++) {
+            if (state == mFoldedDeviceStates[i]) {
+                folded = true;
+                break;
+            }
+        }
+        setDeviceFoldedLocked(folded);
     }
 
     void setDeviceFoldedLocked(boolean isFolded) {
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 4706edc..88b2668 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -173,6 +173,7 @@
     private ContentObserver mContentObserver;
 
     private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
+    private ReduceBrightColorsListener mReduceBrightColorsListener;
 
     private NightDisplayAutoMode mNightDisplayAutoMode;
 
@@ -617,18 +618,24 @@
         if (mCurrentUser == UserHandle.USER_NULL) {
             return;
         }
-        mReduceBrightColorsTintController.setActivated(
-                Secure.getIntForUser(getContext().getContentResolver(),
-                        Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser) == 1);
+        final boolean activated = Secure.getIntForUser(getContext().getContentResolver(),
+                Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser) == 1;
+        mReduceBrightColorsTintController.setActivated(activated);
+        if (mReduceBrightColorsListener != null) {
+            mReduceBrightColorsListener.onReduceBrightColorsActivationChanged(activated);
+        }
     }
 
     private void onReduceBrightColorsStrengthLevelChanged() {
         if (mCurrentUser == UserHandle.USER_NULL) {
             return;
         }
-        mReduceBrightColorsTintController.setMatrix(
-                Secure.getIntForUser(getContext().getContentResolver(),
-                        Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mCurrentUser));
+        final int strength = Secure.getIntForUser(getContext().getContentResolver(),
+                Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mCurrentUser);
+        mReduceBrightColorsTintController.setMatrix(strength);
+        if (mReduceBrightColorsListener != null) {
+            mReduceBrightColorsListener.onReduceBrightColorsStrengthChanged(strength);
+        }
     }
 
     /**
@@ -762,6 +769,22 @@
                 mCurrentUser) == 1;
     }
 
+    private boolean setReduceBrightColorsActivatedInternal(boolean activated) {
+        if (mCurrentUser == UserHandle.USER_NULL) {
+            return false;
+        }
+        return Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, activated ? 1 : 0, mCurrentUser);
+    }
+
+    private boolean setReduceBrightColorsStrengthInternal(int strength) {
+        if (mCurrentUser == UserHandle.USER_NULL) {
+            return false;
+        }
+        return Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.REDUCE_BRIGHT_COLORS_LEVEL, strength, mCurrentUser);
+    }
+
     private boolean isDeviceColorManagedInternal() {
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
         return dtm.isDeviceColorManaged();
@@ -1469,6 +1492,31 @@
         }
 
         /**
+         * Sets the listener and returns whether reduce bright colors is currently enabled.
+         */
+        public boolean setReduceBrightColorsListener(ReduceBrightColorsListener listener) {
+            mReduceBrightColorsListener = listener;
+            return mReduceBrightColorsTintController.isActivated();
+        }
+
+        /**
+         * Returns whether reduce bright colors is currently active.
+         */
+        public boolean isReduceBrightColorsActivated() {
+            return mReduceBrightColorsTintController.isActivated();
+        }
+
+        /**
+         * Gets the computed brightness, in nits, when the reduce bright colors feature is applied
+         * at the current strength.
+         *
+         * @hide
+         */
+        public float getReduceBrightColorsAdjustedBrightnessNits(float nits) {
+            return mReduceBrightColorsTintController.getAdjustedBrightness(nits);
+        }
+
+        /**
          * Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
          * invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
          */
@@ -1491,6 +1539,22 @@
         void onDisplayWhiteBalanceStatusChanged(boolean activated);
     }
 
+    /**
+     * Listener for changes in reduce bright colors status.
+     */
+    public interface ReduceBrightColorsListener {
+
+        /**
+         * Notify that the reduce bright colors activation status has changed.
+         */
+        void onReduceBrightColorsActivationChanged(boolean activated);
+
+        /**
+         * Notify that the reduce bright colors strength has changed.
+         */
+        void onReduceBrightColorsStrengthChanged(int strength);
+    }
+
     private final class TintHandler extends Handler {
 
         private TintHandler(Looper looper) {
@@ -1787,6 +1851,62 @@
         }
 
         @Override
+        public boolean isReduceBrightColorsActivated() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return mReduceBrightColorsTintController.isActivated();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public boolean setReduceBrightColorsActivated(boolean activated) {
+            getContext().enforceCallingOrSelfPermission(
+                    Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+                    "Permission required to set reduce bright colors activation state");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return setReduceBrightColorsActivatedInternal(activated);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public int getReduceBrightColorsStrength() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return mReduceBrightColorsTintController.getStrength();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public float getReduceBrightColorsOffsetFactor() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return mReduceBrightColorsTintController.getOffsetFactor();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public boolean setReduceBrightColorsStrength(int strength) {
+            getContext().enforceCallingOrSelfPermission(
+                    Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+                    "Permission required to set reduce bright colors strength");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return setReduceBrightColorsStrengthInternal(strength);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
                 return;
diff --git a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
index 7e120c9..cb93cc8 100644
--- a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
+++ b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
@@ -34,7 +34,7 @@
 public class ReduceBrightColorsTintController extends TintController {
 
     private final float[] mMatrix = new float[16];
-    private final float[] mCoefficients = new float[9];
+    private final float[] mCoefficients = new float[3];
 
     private int mStrength;
 
@@ -42,8 +42,8 @@
     public void setUp(Context context, boolean needsLinear) {
         final String[] coefficients = context.getResources().getStringArray(
                 needsLinear ? R.array.config_reduceBrightColorsCoefficients
-                        : R.array.config_reduceBrightColorsCoefficientsNative);
-        for (int i = 0; i < 9 && i < coefficients.length; i++) {
+                        : R.array.config_reduceBrightColorsCoefficientsNonlinear);
+        for (int i = 0; i < 3 && i < coefficients.length; i++) {
             mCoefficients[i] = Float.parseFloat(coefficients[i]);
         }
     }
@@ -67,20 +67,11 @@
 
         Matrix.setIdentityM(mMatrix, 0);
 
-        final float percentageStrength = strengthLevel / 100f;
-        final float squaredPercentageStrength = percentageStrength * percentageStrength;
-        final float red =
-                squaredPercentageStrength * mCoefficients[0] + percentageStrength * mCoefficients[1]
-                        + mCoefficients[2];
-        final float green =
-                squaredPercentageStrength * mCoefficients[3] + percentageStrength * mCoefficients[4]
-                        + mCoefficients[5];
-        final float blue =
-                squaredPercentageStrength * mCoefficients[6] + percentageStrength * mCoefficients[7]
-                        + mCoefficients[8];
-        mMatrix[0] = clamp(red);
-        mMatrix[5] = clamp(green);
-        mMatrix[10] = clamp(blue);
+        // All three (r,g,b) components are equal and calculated with the same formula.
+        final float componentValue = computeComponentValue(strengthLevel);
+        mMatrix[0] = componentValue;
+        mMatrix[5] = componentValue;
+        mMatrix[10] = componentValue;
     }
 
     private float clamp(float value) {
@@ -110,4 +101,26 @@
     public int getStrength() {
         return mStrength;
     }
+
+    /** Returns the offset factor at Ymax. */
+    public float getOffsetFactor() {
+        // Strength terms drop out as strength --> 1, leaving the coefficients.
+        return mCoefficients[0] + mCoefficients[1] + mCoefficients[2];
+    }
+
+    /**
+     * Returns the effective brightness (in nits), which has been adjusted to account for the effect
+     * of the bright color reduction.
+     */
+    public float getAdjustedBrightness(float nits) {
+        return computeComponentValue(mStrength) * nits;
+    }
+
+    private float computeComponentValue(int strengthLevel) {
+        final float percentageStrength = strengthLevel / 100f;
+        final float squaredPercentageStrength = percentageStrength * percentageStrength;
+        return clamp(
+                squaredPercentageStrength * mCoefficients[0] + percentageStrength * mCoefficients[1]
+                        + mCoefficients[2]);
+    }
 }
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 9036812..3e2b5ab 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -24,8 +24,8 @@
 import android.graphics.fonts.FontFamily;
 import android.graphics.fonts.FontFileUtil;
 import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontUpdateRequest;
 import android.graphics.fonts.SystemFonts;
-import android.os.ParcelFileDescriptor;
 import android.os.ResultReceiver;
 import android.os.SharedMemory;
 import android.os.ShellCallback;
@@ -54,6 +54,7 @@
 import java.nio.channels.FileChannel;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
@@ -71,14 +72,15 @@
     }
 
     @Override
-    public int updateFont(ParcelFileDescriptor fd, byte[] signature, int baseVersion) {
-        Objects.requireNonNull(fd);
-        Objects.requireNonNull(signature);
+    public int updateFont(int baseVersion, @NonNull FontUpdateRequest request) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(request.getFd());
+        Objects.requireNonNull(request.getSignature());
         Preconditions.checkArgumentNonnegative(baseVersion);
         getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
                 "UPDATE_FONTS permission required.");
         try {
-            installFontFile(fd.getFileDescriptor(), signature, baseVersion);
+            update(baseVersion, Collections.singletonList(request));
             return FontManager.RESULT_SUCCESS;
         } catch (SystemFontException e) {
             Slog.e(TAG, "Failed to update font file", e);
@@ -219,7 +221,13 @@
     private void initialize() {
         synchronized (mUpdatableFontDirLock) {
             if (mUpdatableFontDir == null) {
-                updateSerializedFontMap();
+                synchronized (mSerializedFontMapLock) {
+                    try {
+                        mSerializedFontMap = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+                    } catch (IOException | ErrnoException e) {
+                        mSerializedFontMap = null;
+                    }
+                }
                 return;
             }
             if (mFontCrashDetector.hasCrashed()) {
@@ -249,7 +257,7 @@
         }
     }
 
-    /* package */ void installFontFile(FileDescriptor fd, byte[] pkcs7Signature, int baseVersion)
+    /* package */ void update(int baseVersion, List<FontUpdateRequest> requests)
             throws SystemFontException {
         if (mUpdatableFontDir == null) {
             throw new SystemFontException(
@@ -265,7 +273,7 @@
                         "The base config version is older than current.");
             }
             try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
-                mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
+                mUpdatableFontDir.update(requests);
                 updateSerializedFontMap();
             }
         }
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index 2029f39..cf9a79f 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -24,6 +24,7 @@
 import android.graphics.fonts.Font;
 import android.graphics.fonts.FontFamily;
 import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontUpdateRequest;
 import android.graphics.fonts.FontVariationAxis;
 import android.graphics.fonts.SystemFonts;
 import android.os.Binder;
@@ -44,6 +45,7 @@
 import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -315,6 +317,7 @@
                     "Signature file argument is required.");
         }
 
+        // TODO: close fontFd and sigFd.
         ParcelFileDescriptor fontFd = shell.openFileForSystem(fontPath, "r");
         if (fontFd == null) {
             throw new SystemFontException(
@@ -330,29 +333,24 @@
         }
 
         try (FileInputStream sigFis = new FileInputStream(sigFd.getFileDescriptor())) {
-            try (FileInputStream fontFis = new FileInputStream(fontFd.getFileDescriptor())) {
-                int len = sigFis.available();
-                if (len > MAX_SIGNATURE_FILE_SIZE_BYTES) {
-                    throw new SystemFontException(
-                            FontManager.RESULT_ERROR_SIGNATURE_TOO_LARGE,
-                            "Signature file is too large");
-                }
-                byte[] signature = new byte[len];
-                if (sigFis.read(signature, 0, len) != len) {
-                    throw new SystemFontException(
-                            FontManager.RESULT_ERROR_INVALID_SIGNATURE_FILE,
-                            "Invalid read length");
-                }
-                mService.installFontFile(fontFis.getFD(), signature, -1);
-            } catch (IOException e) {
+            int len = sigFis.available();
+            if (len > MAX_SIGNATURE_FILE_SIZE_BYTES) {
+                throw new SystemFontException(
+                        FontManager.RESULT_ERROR_SIGNATURE_TOO_LARGE,
+                        "Signature file is too large");
+            }
+            byte[] signature = new byte[len];
+            if (sigFis.read(signature, 0, len) != len) {
                 throw new SystemFontException(
                         FontManager.RESULT_ERROR_INVALID_SIGNATURE_FILE,
-                        "Failed to read signature file.", e);
+                        "Invalid read length");
             }
+            mService.update(
+                    -1, Collections.singletonList(new FontUpdateRequest(fontFd, signature)));
         } catch (IOException e) {
             throw new SystemFontException(
-                    FontManager.RESULT_ERROR_INVALID_FONT_FILE,
-                    "Failed to read font files.", e);
+                    FontManager.RESULT_ERROR_INVALID_SIGNATURE_FILE,
+                    "Failed to read signature file.", e);
         }
 
         shell.getOutPrintWriter().println("Success");  // TODO: Output more details.
diff --git a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
index f0d14ba..017f11c 100644
--- a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
+++ b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
@@ -29,24 +30,19 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Set;
 
 /* package */ class PersistentSystemFontConfig {
     private static final String TAG = "PersistentSystemFontConfig";
 
     private static final String TAG_ROOT = "fontConfig";
     private static final String TAG_LAST_MODIFIED_DATE = "lastModifiedDate";
-    private static final String TAG_VALUE = "value";
+    private static final String TAG_UPDATED_FONT_DIR = "updatedFontDir";
+    private static final String ATTR_VALUE = "value";
 
     /* package */ static class Config {
         public long lastModifiedDate;
-
-        public void reset() {
-            lastModifiedDate = 0;
-        }
-
-        public void copyTo(@NonNull Config out) {
-            out.lastModifiedDate = lastModifiedDate;
-        }
+        public final Set<String> updatedFontDirs = new ArraySet<>();
     }
 
     /**
@@ -54,7 +50,6 @@
      */
     public static void loadFromXml(@NonNull InputStream is, @NonNull Config out)
             throws XmlPullParserException, IOException {
-        out.reset();
         TypedXmlPullParser parser = Xml.resolvePullParser(is);
 
         int type;
@@ -72,7 +67,10 @@
             } else if (depth == 2) {
                 switch (tag) {
                     case TAG_LAST_MODIFIED_DATE:
-                        out.lastModifiedDate = parseLongAttribute(parser, TAG_VALUE, 0);
+                        out.lastModifiedDate = parseLongAttribute(parser, ATTR_VALUE, 0);
+                        break;
+                    case TAG_UPDATED_FONT_DIR:
+                        out.updatedFontDirs.add(getAttribute(parser, ATTR_VALUE));
                         break;
                     default:
                         Slog.w(TAG, "Skipping unknown tag: " + tag);
@@ -92,8 +90,13 @@
 
         out.startTag(null, TAG_ROOT);
         out.startTag(null, TAG_LAST_MODIFIED_DATE);
-        out.attribute(null, TAG_VALUE, Long.toString(config.lastModifiedDate));
+        out.attribute(null, ATTR_VALUE, Long.toString(config.lastModifiedDate));
         out.endTag(null, TAG_LAST_MODIFIED_DATE);
+        for (String dir : config.updatedFontDirs) {
+            out.startTag(null, TAG_UPDATED_FONT_DIR);
+            out.attribute(null, ATTR_VALUE, dir);
+            out.endTag(null, TAG_UPDATED_FONT_DIR);
+        }
         out.endTag(null, TAG_ROOT);
 
         out.endDocument();
@@ -111,4 +114,9 @@
         }
     }
 
+    @NonNull
+    private static String getAttribute(TypedXmlPullParser parser, String attr) {
+        final String value = parser.getAttributeValue(null /* namespace */, attr);
+        return value == null ? "" : value;
+    }
 }
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 8fac086..dac94f6 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -21,11 +21,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontUpdateRequest;
 import android.graphics.fonts.SystemFonts;
 import android.os.FileUtils;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.FontConfig;
+import android.util.ArrayMap;
 import android.util.Base64;
 import android.util.Slog;
 
@@ -38,7 +40,6 @@
 import java.io.IOException;
 import java.security.SecureRandom;
 import java.time.Instant;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -72,15 +73,6 @@
         boolean rename(File src, File dest);
     }
 
-    /** Interface to mock persistent configuration */
-    interface PersistentConfig {
-        void loadFromXml(PersistentSystemFontConfig.Config out)
-                throws XmlPullParserException, IOException;
-        void writeToXml(PersistentSystemFontConfig.Config config)
-                throws IOException;
-        boolean rename(File src, File dest);
-    }
-
     /** Data class to hold font file path and revision. */
     private static final class FontFileInfo {
         private final File mFile;
@@ -116,9 +108,7 @@
     private final File mConfigFile;
     private final File mTmpConfigFile;
 
-    private final PersistentSystemFontConfig.Config mConfig =
-            new PersistentSystemFontConfig.Config();
-
+    private long mLastModifiedDate;
     private int mConfigVersion = 1;
 
     /**
@@ -126,7 +116,7 @@
      * FontFileInfo}. All files in this map are validated, and have higher revision numbers than
      * corresponding font files in {@link #mPreinstalledFontDirs}.
      */
-    private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>();
+    private final ArrayMap<String, FontFileInfo> mFontFileInfoMap = new ArrayMap<>();
 
     UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
             FsverityUtil fsverityUtil) {
@@ -145,22 +135,36 @@
     }
 
     /* package */ void loadFontFileMap() {
-        boolean success = false;
-
-        try (FileInputStream fis = new FileInputStream(mConfigFile)) {
-            PersistentSystemFontConfig.loadFromXml(fis, mConfig);
-        } catch (IOException | XmlPullParserException e) {
-            mConfig.reset();
-        }
-
         mFontFileInfoMap.clear();
+        mLastModifiedDate = 0;
+        boolean success = false;
         try {
+            PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+            try (FileInputStream fis = new FileInputStream(mConfigFile)) {
+                PersistentSystemFontConfig.loadFromXml(fis, config);
+            } catch (IOException | XmlPullParserException e) {
+                Slog.e(TAG, "Failed to load config xml file", e);
+                return;
+            }
+            mLastModifiedDate = config.lastModifiedDate;
+
             File[] dirs = mFilesDir.listFiles();
             if (dirs == null) return;
             for (File dir : dirs) {
-                if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) return;
+                if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) {
+                    Slog.e(TAG, "Unexpected dir found: " + dir);
+                    return;
+                }
+                if (!config.updatedFontDirs.contains(dir.getName())) {
+                    Slog.i(TAG, "Deleting obsolete dir: " + dir);
+                    FileUtils.deleteContentsAndDir(dir);
+                    continue;
+                }
                 File[] files = dir.listFiles();
-                if (files == null || files.length != 1) return;
+                if (files == null || files.length != 1) {
+                    Slog.e(TAG, "Unexpected files in dir: " + dir);
+                    return;
+                }
                 FontFileInfo fontFileInfo = validateFontFile(files[0]);
                 addFileToMapIfNewer(fontFileInfo, true /* deleteOldFile */);
             }
@@ -173,6 +177,7 @@
             // Delete all files just in case if we find a problematic file.
             if (!success) {
                 mFontFileInfoMap.clear();
+                mLastModifiedDate = 0;
                 FileUtils.deleteContents(mFilesDir);
             }
         }
@@ -182,10 +187,9 @@
         mFontFileInfoMap.clear();
         FileUtils.deleteContents(mFilesDir);
 
-        mConfig.reset();
-        mConfig.lastModifiedDate = Instant.now().getEpochSecond();
+        mLastModifiedDate = Instant.now().getEpochSecond();
         try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
-            PersistentSystemFontConfig.writeToXml(fos, mConfig);
+            PersistentSystemFontConfig.writeToXml(fos, getPersistentConfig());
         } catch (Exception e) {
             throw new SystemFontException(
                     FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
@@ -195,6 +199,47 @@
     }
 
     /**
+     * Applies multiple {@link FontUpdateRequest}s in transaction.
+     * If one of the request fails, the fonts and config are rolled back to the previous state
+     * before this method is called.
+     */
+    public void update(List<FontUpdateRequest> requests) throws SystemFontException {
+        // Backup the mapping for rollback.
+        ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap);
+        long backupLastModifiedDate = mLastModifiedDate;
+        boolean success = false;
+        try {
+            for (FontUpdateRequest request : requests) {
+                installFontFile(request.getFd().getFileDescriptor(), request.getSignature());
+            }
+
+            // Write config file.
+            mLastModifiedDate = Instant.now().getEpochSecond();
+            try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
+                PersistentSystemFontConfig.writeToXml(fos, getPersistentConfig());
+            } catch (Exception e) {
+                throw new SystemFontException(
+                        FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+                        "Failed to write config XML.", e);
+            }
+
+            if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
+                throw new SystemFontException(
+                        FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+                        "Failed to stage the config file.");
+            }
+            mConfigVersion++;
+            success = true;
+        } finally {
+            if (!success) {
+                mFontFileInfoMap.clear();
+                mFontFileInfoMap.putAll(backupMap);
+                mLastModifiedDate = backupLastModifiedDate;
+            }
+        }
+    }
+
+    /**
      * Installs a new font file, or updates an existing font file.
      *
      * <p>The new font will be immediately available for new Zygote-forked processes through
@@ -205,7 +250,8 @@
      * @param pkcs7Signature A PKCS#7 detached signature to enable fs-verity for the font file.
      * @throws SystemFontException if error occurs.
      */
-    void installFontFile(FileDescriptor fd, byte[] pkcs7Signature) throws SystemFontException {
+    private void installFontFile(FileDescriptor fd, byte[] pkcs7Signature)
+            throws SystemFontException {
         File newDir = getRandomDir(mFilesDir);
         if (!newDir.mkdir()) {
             throw new SystemFontException(
@@ -268,42 +314,11 @@
                         "Failed to change mode to 711", e);
             }
             FontFileInfo fontFileInfo = validateFontFile(newFontFile);
-
-            // Write config file.
-            PersistentSystemFontConfig.Config copied = new PersistentSystemFontConfig.Config();
-            mConfig.copyTo(copied);
-
-            copied.lastModifiedDate = Instant.now().getEpochSecond();
-            try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
-                PersistentSystemFontConfig.writeToXml(fos, copied);
-            } catch (Exception e) {
-                throw new SystemFontException(
-                        FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
-                        "Failed to write config XML.", e);
-            }
-
-            // Backup the mapping for rollback.
-            HashMap<String, FontFileInfo> backup = new HashMap<>(mFontFileInfoMap);
             if (!addFileToMapIfNewer(fontFileInfo, false)) {
                 throw new SystemFontException(
                         FontManager.RESULT_ERROR_DOWNGRADING,
                         "Downgrading font file is forbidden.");
             }
-
-            if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
-                // If we fail to stage the config file, need to rollback the config.
-                mFontFileInfoMap.clear();
-                mFontFileInfoMap.putAll(backup);
-                throw new SystemFontException(
-                        FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
-                        "Failed to stage the config file.");
-            }
-
-
-            // Now font update is succeeded. Update config version.
-            copied.copyTo(mConfig);
-            mConfigVersion++;
-
             success = true;
         } finally {
             if (!success) {
@@ -439,8 +454,17 @@
         }
     }
 
+    private PersistentSystemFontConfig.Config getPersistentConfig() {
+        PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+        config.lastModifiedDate = mLastModifiedDate;
+        for (FontFileInfo info : mFontFileInfoMap.values()) {
+            config.updatedFontDirs.add(info.getRandomizedFontDir().getName());
+        }
+        return config;
+    }
+
     Map<String, File> getFontFileMap() {
-        Map<String, File> map = new HashMap<>();
+        Map<String, File> map = new ArrayMap<>();
         for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
             map.put(entry.getKey(), entry.getValue().getFile());
         }
@@ -448,11 +472,7 @@
     }
 
     /* package */ FontConfig getSystemFontConfig() {
-        return SystemFonts.getSystemFontConfig(
-                getFontFileMap(),
-                mConfig.lastModifiedDate,
-                mConfigVersion
-        );
+        return SystemFonts.getSystemFontConfig(getFontFileMap(), mLastModifiedDate, mConfigVersion);
     }
 
     /* package */ int getConfigVersion() {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e5b5350..d8e124a0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -110,7 +110,6 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -300,45 +299,6 @@
     private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
             "com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
 
-    /**
-     * Debug flag for overriding runtime {@link SystemProperties}.
-     */
-    @AnyThread
-    private static final class DebugFlag {
-        private static final Object LOCK = new Object();
-        private final String mKey;
-        private final boolean mDefaultValue;
-        @GuardedBy("LOCK")
-        private boolean mValue;
-
-        public DebugFlag(String key, boolean defaultValue) {
-            mKey = key;
-            mDefaultValue = defaultValue;
-            mValue = SystemProperties.getBoolean(key, defaultValue);
-        }
-
-        void refresh() {
-            synchronized (LOCK) {
-                mValue = SystemProperties.getBoolean(mKey, mDefaultValue);
-            }
-        }
-
-        boolean value() {
-            synchronized (LOCK) {
-                return mValue;
-            }
-        }
-    }
-
-    /**
-     * Debug flags that can be overridden using "adb shell setprop <key>"
-     * Note: These flags are cached. To refresh, run "adb shell ime refresh_debug_properties".
-     */
-    private static final class DebugFlags {
-        static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
-                new DebugFlag("debug.optimize_startinput", false);
-    }
-
     @UserIdInt
     private int mLastSwitchUserId;
 
@@ -3687,12 +3647,9 @@
                     }
                     res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
                             startInputFlags, startInputReason);
-                } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
-                        || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
+                } else {
                     res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
                             startInputFlags, startInputReason);
-                } else {
-                    res = InputBindResult.NO_EDITOR;
                 }
             } else {
                 res = InputBindResult.NULL_EDITOR_INFO;
@@ -5467,10 +5424,6 @@
         @BinderThread
         @ShellCommandResult
         private int onCommandWithSystemIdentity(@Nullable String cmd) {
-            if ("refresh_debug_properties".equals(cmd)) {
-                return refreshDebugProperties();
-            }
-
             if ("get-last-switch-user-id".equals(cmd)) {
                 return mService.getLastSwitchUserId(this);
             }
@@ -5505,13 +5458,6 @@
         }
 
         @BinderThread
-        @ShellCommandResult
-        private int refreshDebugProperties() {
-            DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
-            return ShellCommandResult.SUCCESS;
-        }
-
-        @BinderThread
         @Override
         public void onHelp() {
             try (PrintWriter pw = getOutPrintWriter()) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 02a36dc..f646d5d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -220,7 +220,8 @@
      */
     @VisibleForTesting
     public Context getSettingsContext(int displayId) {
-        if (mSettingsContext == null) {
+        // TODO(b/178462039): Cover the case when IME is moved to another ImeContainer.
+        if (mSettingsContext == null || mSettingsContext.getDisplayId() != displayId) {
             final Context systemUiContext = ActivityThread.currentActivityThread()
                     .createSystemUiContext(displayId);
             final Context windowContext = systemUiContext.createWindowContext(
@@ -229,11 +230,6 @@
                     windowContext, com.android.internal.R.style.Theme_DeviceDefault_Settings);
             mSwitchingDialogToken = mSettingsContext.getWindowContextToken();
         }
-        // TODO(b/159767464): register the listener to another display again if window token is not
-        // yet created.
-        if (mSettingsContext.getDisplayId() != displayId) {
-            mWindowManagerInternal.moveWindowTokenToDisplay(mSwitchingDialogToken, displayId);
-        }
         return mSettingsContext;
     }
 
diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
similarity index 98%
rename from services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
rename to services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index fe51d74..b5746bb 100644
--- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.utils.eventlog;
+package com.android.server.location.eventlog;
 
 import android.os.SystemClock;
 import android.util.TimeUtils;
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index d16267f..9216a6b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -103,7 +103,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -1489,7 +1488,7 @@
         }
 
         if (locations.length > 0) {
-            reportLocation(LocationResult.create(Arrays.asList(locations)).validate());
+            reportLocation(LocationResult.wrap(locations).validate());
         }
 
         for (Runnable listener : listeners) {
diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/injector/LocationEventLog.java
index b8b54b3..8d73518 100644
--- a/services/core/java/com/android/server/location/injector/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/injector/LocationEventLog.java
@@ -31,7 +31,7 @@
 import android.os.Build;
 import android.os.PowerManager.LocationPowerSaveMode;
 
-import com.android.server.utils.eventlog.LocalEventLog;
+import com.android.server.location.eventlog.LocalEventLog;
 
 /** In memory event log for location events. */
 public class LocationEventLog extends LocalEventLog {
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 06ca9ec..221d4fb 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -20,8 +20,8 @@
 import static android.location.LocationManager.DELIVER_HISTORICAL_LOCATIONS;
 import static android.location.LocationManager.GPS_PROVIDER;
 import static android.location.LocationManager.KEY_FLUSH_COMPLETE;
+import static android.location.LocationManager.KEY_LOCATIONS;
 import static android.location.LocationManager.KEY_LOCATION_CHANGED;
-import static android.location.LocationManager.KEY_LOCATION_RESULT;
 import static android.location.LocationManager.KEY_PROVIDER_ENABLED;
 import static android.location.LocationManager.PASSIVE_PROVIDER;
 import static android.os.IPowerManager.LOCATION_MODE_NO_CHANGE;
@@ -187,7 +187,8 @@
         @Override
         public void deliverOnLocationChanged(LocationResult locationResult,
                 @Nullable Runnable onCompleteCallback) throws RemoteException {
-            mListener.onLocationChanged(locationResult, SingleUseCallback.wrap(onCompleteCallback));
+            mListener.onLocationChanged(locationResult.asList(),
+                    SingleUseCallback.wrap(onCompleteCallback));
         }
 
         @Override
@@ -222,12 +223,16 @@
             // allows apps to start a fg service in response to a location PI
             options.setTemporaryAppWhitelistDuration(TEMPORARY_APP_ALLOWLIST_DURATION_MS);
 
+            Intent intent = new Intent().putExtra(KEY_LOCATION_CHANGED,
+                    locationResult.getLastLocation());
+            if (locationResult.size() > 1) {
+                intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
+            }
+
             mPendingIntent.send(
                     mContext,
                     0,
-                    new Intent()
-                            .putExtra(KEY_LOCATION_CHANGED, locationResult.getLastLocation())
-                            .putExtra(KEY_LOCATION_RESULT, locationResult),
+                    intent,
                     onCompleteCallback != null ? (pI, i, rC, rD, rE) -> onCompleteCallback.run()
                             : null,
                     null,
diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
index dce7b08..0d8f643 100644
--- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
@@ -53,7 +53,7 @@
         Location location = new Location(l);
         location.setIsFromMockProvider(true);
         mLocation = location;
-        reportLocation(LocationResult.wrap(location));
+        reportLocation(LocationResult.wrap(location).validate());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index c274c28..32d637f 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.location.Location;
 import android.location.LocationResult;
 import android.location.provider.ILocationProvider;
 import android.location.provider.ILocationProviderManager;
@@ -40,6 +41,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -260,13 +262,25 @@
 
         // executed on binder thread
         @Override
-        public void onReportLocation(LocationResult locationResult) {
+        public void onReportLocation(Location location) {
             synchronized (mLock) {
                 if (mProxy != this) {
                     return;
                 }
 
-                reportLocation(locationResult.validate());
+                reportLocation(LocationResult.wrap(location).validate());
+            }
+        }
+
+        // executed on binder thread
+        @Override
+        public void onReportLocations(List<Location> locations) {
+            synchronized (mLock) {
+                if (mProxy != this) {
+                    return;
+                }
+
+                reportLocation(LocationResult.wrap(locations).validate());
             }
         }
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index f92f3dc..39ed7e8 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,8 +16,6 @@
 
 package com.android.server.net;
 
-import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
-
 import android.annotation.NonNull;
 import android.net.Network;
 import android.net.NetworkTemplate;
@@ -39,28 +37,6 @@
     public abstract void resetUserState(int userId);
 
     /**
-     * Figure out if networking is blocked for a given set of conditions.
-     *
-     * This is used by ConnectivityService via passing stale copies of conditions, so it must not
-     * take any locks.
-     *
-     * @param uid The target uid.
-     * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
-     * @param isNetworkMetered True if the network is metered.
-     * @param isBackgroundRestricted True if data saver is enabled.
-     *
-     * @return true if networking is blocked for the UID under the specified conditions.
-     */
-    public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
-            boolean isBackgroundRestricted) {
-        // Log of invoking internal function is disabled because it will be called very
-        // frequently. And metrics are unlikely needed on this method because the callers are
-        // external and this method doesn't take any locks or perform expensive operations.
-        return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
-                isBackgroundRestricted, null);
-    }
-
-    /**
      * Informs that an appId has been added or removed from the temp-powersave-allowlist so that
      * that network rules for that appId can be updated.
      *
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 504eefe..b99a552 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1254,7 +1254,7 @@
                 // identified carrier, which may want to manage their own notifications. This method
                 // should be called every time the carrier config changes anyways, and there's no
                 // reason to alert if there isn't a carrier.
-                return;
+                continue;
             }
 
             final boolean notifyWarning = getBooleanDefeatingNullable(config,
@@ -5402,6 +5402,17 @@
     }
 
     @Override
+    public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+            boolean isNetworkMetered, boolean isBackgroundRestricted) {
+        mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
+        // Log of invoking this function is disabled because it will be called very frequently. And
+        // metrics are unlikely needed on this method because the callers are external and this
+        // method doesn't take any locks or perform expensive operations.
+        return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+                isBackgroundRestricted, null);
+    }
+
+    @Override
     public boolean isUidRestrictedOnMeteredNetworks(int uid) {
         mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
         final int uidRules;
@@ -5410,9 +5421,9 @@
             uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
             isBackgroundRestricted = mRestrictBackground;
         }
-        //TODO(b/177490332): The logic here might not be correct because it doesn't consider
-        // RULE_REJECT_METERED condition. And it could be replaced by
-        // isUidNetworkingBlockedInternal().
+        // TODO(b/177490332): The logic here might not be correct because it doesn't consider
+        //  RULE_REJECT_METERED condition. And it could be replaced by
+        //  isUidNetworkingBlockedInternal().
         return isBackgroundRestricted
                 && !hasRule(uidRules, RULE_ALLOW_METERED)
                 && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 461d519..619fc4e 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -23,8 +23,8 @@
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index de77372..18c689f 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,8 +21,8 @@
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
 import static android.service.notification.DNDModeProto.ROOT_CONFIG;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 
 import android.app.AppOpsManager;
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
new file mode 100644
index 0000000..a83edb7
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import android.annotation.AppIdInt;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.FileObserver;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoInputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.BootReceiver;
+import com.android.server.ServiceThread;
+import com.android.server.os.TombstoneProtos.Tombstone;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Optional;
+
+/**
+ * A class to manage native tombstones.
+ */
+public final class NativeTombstoneManager {
+    private static final String TAG = NativeTombstoneManager.class.getSimpleName();
+
+    private static final File TOMBSTONE_DIR = new File("/data/tombstones");
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final TombstoneWatcher mWatcher;
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final SparseArray<TombstoneFile> mTombstones;
+
+    NativeTombstoneManager(Context context) {
+        mTombstones = new SparseArray<TombstoneFile>();
+        mContext = context;
+
+        final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher",
+                THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+        thread.start();
+        mHandler = thread.getThreadHandler();
+
+        mWatcher = new TombstoneWatcher();
+        mWatcher.startWatching();
+    }
+
+    void onSystemReady() {
+        // Scan existing tombstones.
+        mHandler.post(() -> {
+            final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
+            for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
+                if (tombstoneFiles[i].isFile()) {
+                    handleTombstone(tombstoneFiles[i]);
+                }
+            }
+        });
+    }
+
+    private void handleTombstone(File path) {
+        final String filename = path.getName();
+        if (!filename.startsWith("tombstone_")) {
+            return;
+        }
+
+        if (filename.endsWith(".pb")) {
+            handleProtoTombstone(path);
+        } else {
+            BootReceiver.addTombstoneToDropBox(mContext, path);
+        }
+    }
+
+    private void handleProtoTombstone(File path) {
+        final String filename = path.getName();
+        if (!filename.endsWith(".pb")) {
+            Slog.w(TAG, "unexpected tombstone name: " + path);
+            return;
+        }
+
+        final String suffix = filename.substring("tombstone_".length());
+        final String numberStr = suffix.substring(0, suffix.length() - 3);
+
+        int number;
+        try {
+            number = Integer.parseInt(numberStr);
+            if (number < 0 || number > 99) {
+                Slog.w(TAG, "unexpected tombstone name: " + path);
+                return;
+            }
+        } catch (NumberFormatException ex) {
+            Slog.w(TAG, "unexpected tombstone name: " + path);
+            return;
+        }
+
+        ParcelFileDescriptor pfd;
+        try {
+            pfd = ParcelFileDescriptor.open(path, MODE_READ_WRITE);
+        } catch (FileNotFoundException ex) {
+            Slog.w(TAG, "failed to open " + path, ex);
+            return;
+        }
+
+        final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
+        if (!parsedTombstone.isPresent()) {
+            IoUtils.closeQuietly(pfd);
+            return;
+        }
+
+        synchronized (mLock) {
+            TombstoneFile previous = mTombstones.get(number);
+            if (previous != null) {
+                previous.dispose();
+            }
+
+            mTombstones.put(number, parsedTombstone.get());
+        }
+    }
+
+    static class TombstoneFile {
+        final ParcelFileDescriptor mPfd;
+
+        final @UserIdInt int mUserId;
+        final @AppIdInt int mAppId;
+
+        boolean mPurged = false;
+
+        TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) {
+            mPfd = pfd;
+            mUserId = userId;
+            mAppId = appId;
+        }
+
+        public boolean matches(Optional<Integer> userId, Optional<Integer> appId) {
+            if (mPurged) {
+                return false;
+            }
+
+            if (userId.isPresent() && userId.get() != mUserId) {
+                return false;
+            }
+
+            if (appId.isPresent() && appId.get() != mAppId) {
+                return false;
+            }
+
+            return true;
+        }
+
+        public void dispose() {
+            IoUtils.closeQuietly(mPfd);
+        }
+
+        static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
+            final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
+            final ProtoInputStream stream = new ProtoInputStream(is);
+
+            int uid = 0;
+            String selinuxLabel = "";
+
+            try {
+                while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                    switch (stream.getFieldNumber()) {
+                        case (int) Tombstone.UID:
+                            uid = stream.readInt(Tombstone.UID);
+                            break;
+
+                        case (int) Tombstone.SELINUX_LABEL:
+                            selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
+                            break;
+
+                        default:
+                            break;
+                    }
+                }
+            } catch (IOException ex) {
+                Slog.e(TAG, "Failed to parse tombstone", ex);
+                return Optional.empty();
+            }
+
+            if (!UserHandle.isApp(uid)) {
+                Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
+                return Optional.empty();
+            }
+
+            final int userId = UserHandle.getUserId(uid);
+            final int appId = UserHandle.getAppId(uid);
+
+            if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
+                Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
+                return Optional.empty();
+            }
+
+            return Optional.of(new TombstoneFile(pfd, userId, appId));
+        }
+    }
+
+    class TombstoneWatcher extends FileObserver {
+        TombstoneWatcher() {
+            // Tombstones can be created either by linking an O_TMPFILE temporary file (CREATE),
+            // or by moving a named temporary file in the same directory on kernels where O_TMPFILE
+            // isn't supported (MOVED_TO).
+            super(TOMBSTONE_DIR, FileObserver.CREATE | FileObserver.MOVED_TO);
+        }
+
+        @Override
+        public void onEvent(int event, @Nullable String path) {
+            mHandler.post(() -> {
+                handleTombstone(new File(TOMBSTONE_DIR, path));
+            });
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManagerService.java b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
new file mode 100644
index 0000000..cb3c7ff0
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 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.os;
+
+import android.content.Context;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+/**
+ * Service that tracks and manages native tombstones.
+ *
+ * @hide
+ */
+public class NativeTombstoneManagerService extends SystemService {
+    private static final String TAG = "NativeTombstoneManagerService";
+
+    private NativeTombstoneManager mManager;
+
+    public NativeTombstoneManagerService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        mManager = new NativeTombstoneManager(getContext());
+        LocalServices.addService(NativeTombstoneManager.class, mManager);
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+            mManager.onSystemReady();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 5373f99..66ea554 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -23,7 +23,6 @@
 import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
 import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
 import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
 import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
@@ -32,15 +31,15 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
 import android.content.pm.ApkChecksum;
 import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
 import android.content.pm.Signature;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.os.Handler;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.incremental.IncrementalManager;
 import android.os.incremental.IncrementalStorage;
@@ -294,21 +293,21 @@
     /**
      * Fetch or calculate checksums for the collection of files.
      *
-     * @param filesToChecksum       split name, null for base and File to fetch checksums for
-     * @param optional              mask to fetch readily available checksums
-     * @param required              mask to forcefully calculate if not available
-     * @param installerPackageName  package name of the installer of the packages
-     * @param trustedInstallers     array of certificate to trust, two specific cases:
-     *                              null - trust anybody,
-     *                              [] - trust nobody.
-     * @param statusReceiver        to receive the resulting checksums
+     * @param filesToChecksum          split name, null for base and File to fetch checksums for
+     * @param optional                 mask to fetch readily available checksums
+     * @param required                 mask to forcefully calculate if not available
+     * @param installerPackageName     package name of the installer of the packages
+     * @param trustedInstallers        array of certificate to trust, two specific cases:
+     *                                 null - trust anybody,
+     *                                 [] - trust nobody.
+     * @param onChecksumsReadyListener to receive the resulting checksums
      */
     public static void getChecksums(List<Pair<String, File>> filesToChecksum,
             @Checksum.Type int optional,
             @Checksum.Type int required,
             @Nullable String installerPackageName,
             @Nullable Certificate[] trustedInstallers,
-            @NonNull IntentSender statusReceiver,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
             @NonNull Injector injector) {
         List<Map<Integer, ApkChecksum>> result = new ArrayList<>(filesToChecksum.size());
         for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
@@ -326,14 +325,14 @@
         }
 
         long startTime = SystemClock.uptimeMillis();
-        processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector,
-                startTime);
+        processRequiredChecksums(filesToChecksum, result, required, onChecksumsReadyListener,
+                injector, startTime);
     }
 
     private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
             List<Map<Integer, ApkChecksum>> result,
             @Checksum.Type int required,
-            @NonNull IntentSender statusReceiver,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
             @NonNull Injector injector,
             long startTime) {
         final boolean timeout =
@@ -350,7 +349,7 @@
                         // Not ready, come back later.
                         injector.getHandler().postDelayed(() -> {
                             processRequiredChecksums(filesToChecksum, result, required,
-                                    statusReceiver, injector, startTime);
+                                    onChecksumsReadyListener, injector, startTime);
                         }, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS);
                         return;
                     }
@@ -363,13 +362,9 @@
             }
         }
 
-        final Intent intent = new Intent();
-        intent.putExtra(EXTRA_CHECKSUMS,
-                allChecksums.toArray(new ApkChecksum[allChecksums.size()]));
-
         try {
-            statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null);
-        } catch (IntentSender.SendIntentException e) {
+            onChecksumsReadyListener.onChecksumsReady(allChecksums);
+        } catch (RemoteException e) {
             Slog.w(TAG, e);
         }
     }
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
new file mode 100644
index 0000000..a32e107
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.content.IntentFilter;
+
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Representation of an immutable default cross-profile intent filter.
+ */
+@Immutable
+final class DefaultCrossProfileIntentFilter {
+
+    @IntDef({
+            Direction.TO_PARENT,
+            Direction.TO_PROFILE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Direction {
+        int TO_PARENT = 0;
+        int TO_PROFILE = 1;
+    }
+
+    /** The intent filter that's used */
+    public final IntentFilter filter;
+
+    /**
+     * The flags related to the forwarding, e.g.
+     * {@link android.content.pm.PackageManager#SKIP_CURRENT_PROFILE} or
+     * {@link android.content.pm.PackageManager#ONLY_IF_NO_MATCH_FOUND}.
+     */
+    public final int flags;
+
+    /**
+     * The direction of forwarding, can be either {@link Direction#TO_PARENT} or
+     * {@link Direction#TO_PROFILE}.
+     */
+    public final @Direction int direction;
+
+    /**
+     * Whether this cross profile intent filter would allow personal data to be shared into
+     * the work profile. If this is {@code true}, this intent filter should be only added to
+     * the profile if the admin does not enable
+     * {@link android.os.UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE}.
+     */
+    public final boolean letsPersonalDataIntoProfile;
+
+    private DefaultCrossProfileIntentFilter(IntentFilter filter, int flags,
+            @Direction int direction, boolean letsPersonalDataIntoProfile) {
+        this.filter = requireNonNull(filter);
+        this.flags = flags;
+        this.direction = direction;
+        this.letsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+    }
+
+    static final class Builder {
+        private IntentFilter mFilter = new IntentFilter();
+        private int mFlags;
+        private @Direction int mDirection;
+        private boolean mLetsPersonalDataIntoProfile;
+
+        Builder(@Direction int direction, int flags, boolean letsPersonalDataIntoProfile) {
+            mDirection = direction;
+            mFlags = flags;
+            mLetsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+        }
+
+        Builder addAction(String action) {
+            mFilter.addAction(action);
+            return this;
+        }
+
+        Builder addCategory(String category) {
+            mFilter.addCategory(category);
+            return this;
+        }
+
+        Builder addDataType(String type) {
+            try {
+                mFilter.addDataType(type);
+            } catch (IntentFilter.MalformedMimeTypeException e) {
+                // ignore
+            }
+            return this;
+        }
+
+        Builder addDataScheme(String scheme) {
+            mFilter.addDataScheme(scheme);
+            return this;
+        }
+
+        DefaultCrossProfileIntentFilter build() {
+            return new DefaultCrossProfileIntentFilter(mFilter, mFlags, mDirection,
+                    mLetsPersonalDataIntoProfile);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
new file mode 100644
index 0000000..3019439
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.ONLY_IF_NO_MATCH_FOUND;
+import static android.content.pm.PackageManager.SKIP_CURRENT_PROFILE;
+import static android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH;
+
+import android.content.Intent;
+import android.hardware.usb.UsbManager;
+import android.provider.AlarmClock;
+import android.provider.MediaStore;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility Class for {@link DefaultCrossProfileIntentFilter}.
+ */
+public class DefaultCrossProfileIntentFiltersUtils {
+
+    private DefaultCrossProfileIntentFiltersUtils() {
+    }
+
+    // Intents from profile to parent user
+    /** Emergency call intent with mime type is always resolved by primary user. */
+    private static final DefaultCrossProfileIntentFilter
+            EMERGENCY_CALL_MIME =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_CALL_EMERGENCY)
+                    .addAction(Intent.ACTION_CALL_PRIVILEGED)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataType("vnd.android.cursor.item/phone")
+                    .addDataType("vnd.android.cursor.item/phone_v2")
+                    .addDataType("vnd.android.cursor.item/person")
+                    .addDataType("vnd.android.cursor.dir/calls")
+                    .addDataType("vnd.android.cursor.item/calls")
+                    .build();
+
+    /** Emergency call intent with data schemes is always resolved by primary user. */
+    private static final DefaultCrossProfileIntentFilter
+            EMERGENCY_CALL_DATA =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_CALL_EMERGENCY)
+                    .addAction(Intent.ACTION_CALL_PRIVILEGED)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataScheme("tel")
+                    .addDataScheme("sip")
+                    .addDataScheme("voicemail")
+                    .build();
+
+    /** Dial intent with mime type can be handled by either managed profile or its parent user. */
+    private static final DefaultCrossProfileIntentFilter DIAL_MIME =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataType("vnd.android.cursor.item/phone")
+                    .addDataType("vnd.android.cursor.item/phone_v2")
+                    .addDataType("vnd.android.cursor.item/person")
+                    .addDataType("vnd.android.cursor.dir/calls")
+                    .addDataType("vnd.android.cursor.item/calls")
+                    .build();
+
+    /** Dial intent with data scheme can be handled by either managed profile or its parent user. */
+    private static final DefaultCrossProfileIntentFilter DIAL_DATA =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataScheme("tel")
+                    .addDataScheme("sip")
+                    .addDataScheme("voicemail")
+                    .build();
+
+    /**
+     * Dial intent with no data scheme or type can be handled by either managed profile or its
+     * parent user.
+     */
+    private static final DefaultCrossProfileIntentFilter DIAL_RAW =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .build();
+
+    /** Pressing the call button can be handled by either managed profile or its parent user. */
+    private static final DefaultCrossProfileIntentFilter CALL_BUTTON =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_CALL_BUTTON)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** SMS and MMS are exclusively handled by the primary user. */
+    private static final DefaultCrossProfileIntentFilter SMS_MMS =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addAction(Intent.ACTION_SENDTO)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataScheme("sms")
+                    .addDataScheme("smsto")
+                    .addDataScheme("mms")
+                    .addDataScheme("mmsto")
+                    .build();
+
+    /** Mobile network settings is always shown in the primary user. */
+    private static final DefaultCrossProfileIntentFilter
+            MOBILE_NETWORK_SETTINGS =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS)
+                    .addAction(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** HOME intent is always resolved by the primary user. */
+    static final DefaultCrossProfileIntentFilter HOME =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_MAIN)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_HOME)
+                    .build();
+
+    /** Get content can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter GET_CONTENT =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_GET_CONTENT)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_OPENABLE)
+                    .addDataType("*/*")
+                    .build();
+
+    /** Open document intent can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_OPEN_DOCUMENT)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_OPENABLE)
+                    .addDataType("*/*")
+                    .build();
+
+    /** Pick for any data type can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter ACTION_PICK_DATA =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_PICK)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addDataType("*/*")
+                    .build();
+
+    /** Pick without data type can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter ACTION_PICK_RAW =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_PICK)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** Speech recognition can be performed by primary user. */
+    private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(ACTION_RECOGNIZE_SPEECH)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** Media capture can be performed by primary user. */
+    private static final DefaultCrossProfileIntentFilter MEDIA_CAPTURE =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(MediaStore.ACTION_IMAGE_CAPTURE)
+                    .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+                    .addAction(MediaStore.ACTION_VIDEO_CAPTURE)
+                    .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
+                    .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
+                    .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+                    .addAction(MediaStore.INTENT_ACTION_VIDEO_CAMERA)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** Alarm setting can be performed by primary user. */
+    private static final DefaultCrossProfileIntentFilter SET_ALARM =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(AlarmClock.ACTION_SET_ALARM)
+                    .addAction(AlarmClock.ACTION_SHOW_ALARMS)
+                    .addAction(AlarmClock.ACTION_SET_TIMER)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    // Intents from parent to profile user
+
+    /** ACTION_SEND can be forwarded to the managed profile on user's choice. */
+    private static final DefaultCrossProfileIntentFilter ACTION_SEND =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_SEND)
+                    .addAction(Intent.ACTION_SEND_MULTIPLE)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addDataType("*/*")
+                    .build();
+
+    /** USB devices attached can get forwarded to the profile. */
+    private static final DefaultCrossProfileIntentFilter
+            USB_DEVICE_ATTACHED =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
+                    .addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileFilters() {
+        return Arrays.asList(
+                EMERGENCY_CALL_MIME,
+                EMERGENCY_CALL_DATA,
+                DIAL_MIME,
+                DIAL_DATA,
+                DIAL_RAW,
+                CALL_BUTTON,
+                SMS_MMS,
+                SET_ALARM,
+                MEDIA_CAPTURE,
+                RECOGNIZE_SPEECH,
+                ACTION_PICK_RAW,
+                ACTION_PICK_DATA,
+                OPEN_DOCUMENT,
+                GET_CONTENT,
+                USB_DEVICE_ATTACHED,
+                ACTION_SEND,
+                HOME,
+                MOBILE_NETWORK_SETTINGS);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index 4f986bd..2a1fc87 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -33,7 +33,7 @@
     public static final int DUMP_KEYSETS = 1 << 14;
     public static final int DUMP_VERSION = 1 << 15;
     public static final int DUMP_INSTALLS = 1 << 16;
-    public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 17;
+    public static final int DUMP_DOMAIN_VERIFIER = 1 << 17;
     public static final int DUMP_DOMAIN_PREFERRED = 1 << 18;
     public static final int DUMP_FROZEN = 1 << 19;
     public static final int DUMP_DEXOPT = 1 << 20;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6e5bd94..9a84b36 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -177,6 +177,7 @@
 import android.content.pm.FallbackCategoryProvider;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.IPackageChangeObserver;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
@@ -377,6 +378,11 @@
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.dex.ViewCompiler;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationService;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2;
 import com.android.server.pm.parsing.PackageCacher;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.PackageParser2;
@@ -399,6 +405,7 @@
 import com.android.server.utils.Watched;
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedSparseBooleanArray;
+import com.android.server.utils.WatchedSparseIntArray;
 import com.android.server.utils.Watcher;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -460,6 +467,7 @@
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
+import java.util.function.Supplier;
 
 /**
  * Keep track of all those APKs everywhere.
@@ -1063,6 +1071,9 @@
         private final ServiceProducer mGetLocalServiceProducer;
         private final ServiceProducer mGetSystemServiceProducer;
         private final Singleton<ModuleInfoProvider> mModuleInfoProviderProducer;
+        private final Singleton<DomainVerificationManagerInternal>
+                mDomainVerificationManagerInternalProducer;
+        private final Singleton<Handler> mHandlerProducer;
 
         Injector(Context context, Object lock, Installer installer,
                 Object installLock, PackageAbiHelper abiHelper,
@@ -1091,6 +1102,9 @@
                         instantAppResolverConnectionProducer,
                 Producer<ModuleInfoProvider> moduleInfoProviderProducer,
                 Producer<LegacyPermissionManagerInternal> legacyPermissionManagerInternalProducer,
+                Producer<DomainVerificationManagerInternal>
+                        domainVerificationManagerInternalProducer,
+                Producer<Handler> handlerProducer,
                 SystemWrapper systemWrapper,
                 ServiceProducer getLocalServiceProducer,
                 ServiceProducer getSystemServiceProducer) {
@@ -1128,6 +1142,9 @@
             mSystemWrapper = systemWrapper;
             mGetLocalServiceProducer = getLocalServiceProducer;
             mGetSystemServiceProducer = getSystemServiceProducer;
+            mDomainVerificationManagerInternalProducer =
+                    new Singleton<>(domainVerificationManagerInternalProducer);
+            mHandlerProducer = new Singleton<>(handlerProducer);
         }
 
         /**
@@ -1273,6 +1290,14 @@
         public LegacyPermissionManagerInternal getLegacyPermissionManagerInternal() {
             return mLegacyPermissionManagerInternalProducer.get(this, mPackageManager);
         }
+
+        public DomainVerificationManagerInternal getDomainVerificationManagerInternal() {
+            return mDomainVerificationManagerInternalProducer.get(this, mPackageManager);
+        }
+
+        public Handler getHandler() {
+            return mHandlerProducer.get(this, mPackageManager);
+        }
     }
 
     /** Provides an abstraction to static access to system state. */
@@ -1327,8 +1352,6 @@
         public InstantAppRegistry instantAppRegistry;
         public InstantAppResolverConnection instantAppResolverConnection;
         public ComponentName instantAppResolverSettingsComponent;
-        public @Nullable IntentFilterVerifier<ParsedIntentInfo> intentFilterVerifier;
-        public @Nullable ComponentName intentFilterVerifierComponent;
         public boolean isPreNmr1Upgrade;
         public boolean isPreNupgrade;
         public boolean isPreQupgrade;
@@ -1451,10 +1474,8 @@
 
     boolean mResolverReplaced = false;
 
-    private final @Nullable ComponentName mIntentFilterVerifierComponent;
-    private final @Nullable IntentFilterVerifier<ParsedIntentInfo> mIntentFilterVerifier;
-
-    private int mIntentFilterVerificationToken = 0;
+    @NonNull
+    private final DomainVerificationManagerInternal mDomainVerificationManager;
 
     /** The service connection to the ephemeral resolver */
     final InstantAppResolverConnection mInstantAppResolverConnection;
@@ -1470,9 +1491,6 @@
     private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
             mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
 
-    final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
-            = new SparseArray<>();
-
     // Internal interface for permission manager
     private final PermissionManagerServiceInternal mPermissionManager;
 
@@ -1492,262 +1510,6 @@
 
     private final PackageProperty mPackageProperty = new PackageProperty();
 
-    private static class IFVerificationParams {
-        String packageName;
-        boolean hasDomainUrls;
-        List<ParsedActivity> activities;
-        boolean replacing;
-        int userId;
-        int verifierUid;
-
-        public IFVerificationParams(String packageName, boolean hasDomainUrls,
-                List<ParsedActivity> activities, boolean _replacing,
-                int _userId, int _verifierUid) {
-            this.packageName = packageName;
-            this.hasDomainUrls = hasDomainUrls;
-            this.activities = activities;
-            replacing = _replacing;
-            userId = _userId;
-            verifierUid = _verifierUid;
-        }
-    }
-
-    private interface IntentFilterVerifier<T extends IntentFilter> {
-        boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
-                                               T filter, String packageName);
-        void startVerifications(int userId);
-        void receiveVerificationResponse(int verificationId);
-    }
-
-    private class IntentVerifierProxy implements IntentFilterVerifier<ParsedIntentInfo> {
-        private Context mContext;
-        private ComponentName mIntentFilterVerifierComponent;
-        private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<>();
-
-        public IntentVerifierProxy(Context context, ComponentName verifierComponent) {
-            mContext = context;
-            mIntentFilterVerifierComponent = verifierComponent;
-        }
-
-        private String getDefaultScheme() {
-            return IntentFilter.SCHEME_HTTPS;
-        }
-
-        @Override
-        public void startVerifications(int userId) {
-            // Launch verifications requests
-            int count = mCurrentIntentFilterVerifications.size();
-            for (int n=0; n<count; n++) {
-                int verificationId = mCurrentIntentFilterVerifications.get(n);
-                final IntentFilterVerificationState ivs =
-                        mIntentFilterVerificationStates.get(verificationId);
-
-                String packageName = ivs.getPackageName();
-
-                ArrayList<ParsedIntentInfo> filters = ivs.getFilters();
-                final int filterCount = filters.size();
-                ArraySet<String> domainsSet = new ArraySet<>();
-                for (int m=0; m<filterCount; m++) {
-                    ParsedIntentInfo filter = filters.get(m);
-                    domainsSet.addAll(filter.getHostsList());
-                }
-                synchronized (mLock) {
-                    if (mSettings.createIntentFilterVerificationIfNeededLPw(
-                            packageName, domainsSet) != null) {
-                        scheduleWriteSettingsLocked();
-                    }
-                }
-                sendVerificationRequest(verificationId, ivs);
-            }
-            mCurrentIntentFilterVerifications.clear();
-        }
-
-        private void sendVerificationRequest(int verificationId, IntentFilterVerificationState ivs) {
-            Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
-            verificationIntent.putExtra(
-                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
-                    verificationId);
-            verificationIntent.putExtra(
-                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
-                    getDefaultScheme());
-            verificationIntent.putExtra(
-                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
-                    ivs.getHostsString());
-            verificationIntent.putExtra(
-                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
-                    ivs.getPackageName());
-            verificationIntent.setComponent(mIntentFilterVerifierComponent);
-            verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-
-            final long whitelistTimeout = getVerificationTimeout();
-            final BroadcastOptions options = BroadcastOptions.makeBasic();
-            options.setTemporaryAppWhitelistDuration(whitelistTimeout);
-
-            DeviceIdleInternal idleController =
-                    mInjector.getLocalService(DeviceIdleInternal.class);
-            idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
-                    mIntentFilterVerifierComponent.getPackageName(), whitelistTimeout,
-                    UserHandle.USER_SYSTEM, true, "intent filter verifier");
-
-            mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM,
-                    null, options.toBundle());
-            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
-                    "Sending IntentFilter verification broadcast");
-        }
-
-        public void receiveVerificationResponse(int verificationId) {
-            IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
-
-            final boolean verified = ivs.isVerified();
-
-            ArrayList<ParsedIntentInfo> filters = ivs.getFilters();
-            final int count = filters.size();
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.i(TAG, "Received verification response " + verificationId
-                        + " for " + count + " filters, verified=" + verified);
-            }
-            for (int n=0; n<count; n++) {
-                ParsedIntentInfo filter = filters.get(n);
-                filter.setVerified(verified);
-
-                if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "IntentFilter " + filter.toString()
-                        + " verified with result:" + verified + " and hosts:"
-                        + ivs.getHostsString());
-            }
-
-            mIntentFilterVerificationStates.remove(verificationId);
-
-            final String packageName = ivs.getPackageName();
-            IntentFilterVerificationInfo ivi;
-
-            synchronized (mLock) {
-                ivi = mSettings.getIntentFilterVerificationLPr(packageName);
-            }
-            if (ivi == null) {
-                Slog.w(TAG, "IntentFilterVerificationInfo not found for verificationId:"
-                        + verificationId + " packageName:" + packageName);
-                return;
-            }
-
-            synchronized (mLock) {
-                if (verified) {
-                    ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
-                } else {
-                    ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK);
-                }
-                scheduleWriteSettingsLocked();
-
-                final int userId = ivs.getUserId();
-                if (userId != UserHandle.USER_ALL) {
-                    final int userStatus =
-                            mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
-
-                    int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-                    boolean needUpdate = false;
-
-                    // In a success case, we promote from undefined or ASK to ALWAYS.  This
-                    // supports a flow where the app fails validation but then ships an updated
-                    // APK that passes, and therefore deserves to be in ALWAYS.
-                    //
-                    // If validation failed, the undefined state winds up in the basic ASK behavior,
-                    // but apps that previously passed and became ALWAYS are *demoted* out of
-                    // that state, since they would not deserve the ALWAYS behavior in case of a
-                    // clean install.
-                    switch (userStatus) {
-                        case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
-                            if (!verified) {
-                                // Don't demote if sysconfig says 'always'
-                                SystemConfig systemConfig = mInjector.getSystemConfig();
-                                ArraySet<String> packages = systemConfig.getLinkedApps();
-                                if (!packages.contains(packageName)) {
-                                    // updatedStatus is already UNDEFINED
-                                    needUpdate = true;
-
-                                    if (DEBUG_DOMAIN_VERIFICATION) {
-                                        Slog.d(TAG, "Formerly validated but now failing; demoting");
-                                    }
-                                } else {
-                                    if (DEBUG_DOMAIN_VERIFICATION) {
-                                        Slog.d(TAG, "Updating bundled package " + packageName
-                                                + " failed autoVerify, but sysconfig supersedes");
-                                    }
-                                    // leave needUpdate == false here intentionally
-                                }
-                            }
-                            break;
-
-                        case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
-                            // Stay in 'undefined' on verification failure
-                            if (verified) {
-                                updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-                            }
-                            needUpdate = true;
-                            if (DEBUG_DOMAIN_VERIFICATION) {
-                                Slog.d(TAG, "Applying update; old=" + userStatus
-                                        + " new=" + updatedStatus);
-                            }
-                            break;
-
-                        case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
-                            // Keep in 'ask' on failure
-                            if (verified) {
-                                updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-                                needUpdate = true;
-                            }
-                            break;
-
-                        default:
-                            // Nothing to do
-                    }
-
-                    if (needUpdate) {
-                        mSettings.updateIntentFilterVerificationStatusLPw(
-                                packageName, updatedStatus, userId);
-                        scheduleWritePackageRestrictionsLocked(userId);
-                    }
-                } else {
-                    Slog.i(TAG, "autoVerify ignored when installing for all users");
-                }
-            }
-        }
-
-        @Override
-        public boolean addOneIntentFilterVerification(int verifierUid, int userId, int verificationId,
-                ParsedIntentInfo filter, String packageName) {
-            if (!hasValidDomains(filter)) {
-                return false;
-            }
-            IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
-            if (ivs == null) {
-                ivs = createDomainVerificationState(verifierUid, userId, verificationId,
-                        packageName);
-            }
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.d(TAG, "Adding verification filter for " + packageName + ": " + filter);
-            }
-            ivs.addFilter(filter);
-            return true;
-        }
-
-        private IntentFilterVerificationState createDomainVerificationState(int verifierUid,
-                int userId, int verificationId, String packageName) {
-            IntentFilterVerificationState ivs = new IntentFilterVerificationState(
-                    verifierUid, userId, packageName);
-            ivs.setPendingState();
-            synchronized (mLock) {
-                mIntentFilterVerificationStates.append(verificationId, ivs);
-                mCurrentIntentFilterVerifications.add(verificationId);
-            }
-            return ivs;
-        }
-    }
-
-    private static boolean hasValidDomains(ParsedIntentInfo filter) {
-        return filter.hasCategory(Intent.CATEGORY_BROWSABLE)
-                && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
-                        filter.hasDataScheme(IntentFilter.SCHEME_HTTPS));
-    }
-
     // Set of pending broadcasts for aggregating enable/disable of components.
     @VisibleForTesting(visibility = Visibility.PACKAGE)
     public static class PendingPackageBroadcasts {
@@ -1822,8 +1584,8 @@
     static final int WRITE_PACKAGE_RESTRICTIONS = 14;
     static final int PACKAGE_VERIFIED = 15;
     static final int CHECK_PENDING_VERIFICATION = 16;
-    static final int START_INTENT_FILTER_VERIFICATIONS = 17;
-    static final int INTENT_FILTER_VERIFIED = 18;
+    // public static final int UNUSED = 17;
+    // public static final int UNUSED = 18;
     static final int WRITE_PACKAGE_LIST = 19;
     static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;
     static final int ENABLE_ROLLBACK_STATUS = 21;
@@ -1832,6 +1594,7 @@
     static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
     static final int INTEGRITY_VERIFICATION_COMPLETE = 25;
     static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
+    static final int DOMAIN_VERIFICATION = 27;
 
     static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
     static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
@@ -1925,6 +1688,74 @@
     private final PackageUsage mPackageUsage = new PackageUsage();
     private final CompilerStats mCompilerStats = new CompilerStats();
 
+    private final DomainVerificationConnection mDomainVerificationConnection =
+            new DomainVerificationConnection();
+
+    private class DomainVerificationConnection implements
+            DomainVerificationService.Connection, DomainVerificationProxyV1.Connection,
+            DomainVerificationProxyV2.Connection {
+
+        @Override
+        public void scheduleWriteSettings() {
+            synchronized (mLock) {
+                PackageManagerService.this.scheduleWriteSettingsLocked();
+            }
+        }
+
+        @Override
+        public int getCallingUid() {
+            return Binder.getCallingUid();
+        }
+
+        @UserIdInt
+        @Override
+        public int getCallingUserId() {
+            return UserHandle.getCallingUserId();
+        }
+
+        @Override
+        public void schedule(int code, @Nullable Object object) {
+            Message message = mHandler.obtainMessage(DOMAIN_VERIFICATION);
+            message.arg1 = code;
+            message.obj = object;
+            mHandler.sendMessage(message);
+        }
+
+        @Override
+        public long getPowerSaveTempWhitelistAppDuration() {
+            return PackageManagerService.this.getVerificationTimeout();
+        }
+
+        @Override
+        public DeviceIdleInternal getDeviceIdleInternal() {
+            return mInjector.getLocalService(DeviceIdleInternal.class);
+        }
+
+        @Override
+        public boolean isCallerPackage(int callingUid, @NonNull String packageName) {
+            final int callingUserId = UserHandle.getUserId(callingUid);
+            return callingUid == getPackageUid(packageName, 0, callingUserId);
+        }
+
+        @Nullable
+        @Override
+        public PackageSetting getPackageSettingLocked(@NonNull String pkgName) {
+            return PackageManagerService.this.getPackageSetting(pkgName);
+        }
+
+        @Nullable
+        @Override
+        public AndroidPackage getPackageLocked(@NonNull String pkgName) {
+            return PackageManagerService.this.getPackage(pkgName);
+        }
+
+        @Nullable
+        @Override
+        public AndroidPackage getPackage(@NonNull String packageName) {
+            return getPackageLocked(packageName);
+        }
+    }
+
     /**
      * Invalidate the package info cache, which includes updating the cached computer.
      * @hide
@@ -2145,7 +1976,6 @@
                 boolean isImplicitImageCaptureIntentAndNotSetByDpc);
         int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
                 boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc);
-        long getDomainVerificationStatusLPr(PackageSetting ps, int userId);
         void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
                 boolean requireFullPermission, boolean checkShell, String message);
         void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
@@ -2199,6 +2029,7 @@
         private final ComponentResolver mComponentResolver;
         private final InstantAppResolverConnection mInstantAppResolverConnection;
         private final DefaultAppProvider mDefaultAppProvider;
+        private final DomainVerificationManagerInternal mDomainVerificationManager;
 
         // PackageManagerService attributes that are primitives are referenced through the
         // pms object directly.  Primitives are the only attributes so referenced.
@@ -2244,6 +2075,7 @@
             mComponentResolver = args.service.mComponentResolver;
             mInstantAppResolverConnection = args.service.mInstantAppResolverConnection;
             mDefaultAppProvider = args.service.mDefaultAppProvider;
+            mDomainVerificationManager = args.service.mDomainVerificationManager;
 
             // Used to reference PMS attributes that are primitives and which are not
             // updated under control of the PMS lock.
@@ -2755,8 +2587,6 @@
             final ArrayList<ResolveInfo> result = new ArrayList<>();
             final ArrayList<ResolveInfo> alwaysList = new ArrayList<>();
             final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
-            final ArrayList<ResolveInfo> alwaysAskList = new ArrayList<>();
-            final ArrayList<ResolveInfo> neverList = new ArrayList<>();
             final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
             final int count = candidates.size();
             // First, try to use linked apps. Partition the candidates into four lists:
@@ -2772,43 +2602,15 @@
                         matchAllList.add(info);
                         continue;
                     }
-                    // Try to get the status from User settings first
-                    long packedStatus = getDomainVerificationStatusLPr(ps, userId);
-                    int status = (int)(packedStatus >> 32);
-                    int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
-                    if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
-                        if (DEBUG_DOMAIN_VERIFICATION || debug) {
-                            Slog.i(TAG, "  + always: " + info.activityInfo.packageName
-                                    + " : linkgen=" + linkGeneration);
-                        }
 
-                        if (!intent.hasCategory(CATEGORY_BROWSABLE)
-                                || !intent.hasCategory(CATEGORY_DEFAULT)) {
-                            undefinedList.add(info);
-                            continue;
-                        }
-
-                        // Use link-enabled generation as preferredOrder, i.e.
-                        // prefer newly-enabled over earlier-enabled.
-                        info.preferredOrder = linkGeneration;
+                    boolean isAlways = mDomainVerificationManager
+                            .isApprovedForDomain(ps, intent, userId);
+                    if (isAlways) {
                         alwaysList.add(info);
-                    } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
-                        if (DEBUG_DOMAIN_VERIFICATION || debug) {
-                            Slog.i(TAG, "  + never: " + info.activityInfo.packageName);
-                        }
-                        neverList.add(info);
-                    } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
-                        if (DEBUG_DOMAIN_VERIFICATION || debug) {
-                            Slog.i(TAG, "  + always-ask: " + info.activityInfo.packageName);
-                        }
-                        alwaysAskList.add(info);
-                    } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED ||
-                            status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK) {
-                        if (DEBUG_DOMAIN_VERIFICATION || debug) {
-                            Slog.i(TAG, "  + ask: " + info.activityInfo.packageName);
-                        }
+                    } else {
                         undefinedList.add(info);
                     }
+                    continue;
                 }
             }
 
@@ -2822,25 +2624,12 @@
                 // Add all undefined apps as we want them to appear in the disambiguation dialog.
                 result.addAll(undefinedList);
                 // Maybe add one for the other profile.
-                if (xpDomainInfo != null && (
-                        xpDomainInfo.bestDomainVerificationStatus
-                        != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) {
+                if (xpDomainInfo != null && xpDomainInfo.wereAnyDomainsVerificationApproved) {
                     result.add(xpDomainInfo.resolveInfo);
                 }
                 includeBrowser = true;
             }
 
-            // The presence of any 'always ask' alternatives means we'll also offer browsers.
-            // If there were 'always' entries their preferred order has been set, so we also
-            // back that off to make the alternatives equivalent
-            if (alwaysAskList.size() > 0) {
-                for (ResolveInfo i : result) {
-                    i.preferredOrder = 0;
-                }
-                result.addAll(alwaysAskList);
-                includeBrowser = true;
-            }
-
             if (includeBrowser) {
                 // Also add browsers (all of them or only the default one)
                 if (DEBUG_DOMAIN_VERIFICATION) {
@@ -2891,7 +2680,6 @@
                 // has explicitly put into the INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER state
                 if (result.size() == 0) {
                     result.addAll(candidates);
-                    result.removeAll(neverList);
                 }
             }
             return result;
@@ -2985,21 +2773,16 @@
                 if (ps == null) {
                     continue;
                 }
-                long verificationState = getDomainVerificationStatusLPr(ps, parentUserId);
-                int status = (int)(verificationState >> 32);
                 if (result == null) {
                     result = new CrossProfileDomainInfo();
                     result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(),
                             sourceUserId, parentUserId);
-                    result.bestDomainVerificationStatus = status;
-                } else {
-                    result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
-                            result.bestDomainVerificationStatus);
                 }
+
+                result.wereAnyDomainsVerificationApproved |= mDomainVerificationManager
+                        .isApprovedForDomain(ps, intent, riTargetUser.targetUserId);
             }
-            // Don't consider matches with status NEVER across profiles.
-            if (result != null && result.bestDomainVerificationStatus
-                    == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+            if (result != null && !result.wereAnyDomainsVerificationApproved) {
                 return null;
             }
             return result;
@@ -3242,26 +3025,20 @@
                     final String packageName = info.activityInfo.packageName;
                     final PackageSetting ps = mSettings.getPackageLPr(packageName);
                     if (ps.getInstantApp(userId)) {
-                        final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
-                        final int status = (int)(packedStatus >> 32);
-                        if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
-                            // there's a local instant application installed, but, the user has
-                            // chosen to never use it; skip resolution and don't acknowledge
-                            // an instant application is even available
+                        if (mDomainVerificationManager.isApprovedForDomain(ps, intent, userId)) {
                             if (DEBUG_INSTANT) {
-                                Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName);
-                            }
-                            blockResolution = true;
-                            break;
-                        } else {
-                            // we have a locally installed instant application; skip resolution
-                            // but acknowledge there's an instant application available
-                            if (DEBUG_INSTANT) {
-                                Slog.v(TAG, "Found installed instant app; pkg: " + packageName);
+                                Slog.v(TAG, "Instant app approvd for intent; pkg: "
+                                        + packageName);
                             }
                             localInstantApp = info;
-                            break;
+                        } else {
+                            if (DEBUG_INSTANT) {
+                                Slog.v(TAG, "Instant app not approved for intent; pkg: "
+                                        + packageName);
+                            }
+                            blockResolution = true;
                         }
+                        break;
                     }
                 }
             }
@@ -3846,6 +3623,8 @@
             final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
             final int userId = UserHandle.getUserId(uid);
             final int appId = UserHandle.getAppId(uid);
+            enforceCrossUserPermission(callingUid, userId,
+                    /* requireFullPermission */ false, /* checkShell */ false, "getPackagesForUid");
             return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
         }
 
@@ -4175,14 +3954,10 @@
                 if (ps != null) {
                     // only check domain verification status if the app is not a browser
                     if (!info.handleAllWebDataURI) {
-                        // Try to get the status from User settings first
-                        final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
-                        final int status = (int) (packedStatus >> 32);
-                        if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
-                                || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
+                        if (mDomainVerificationManager.isApprovedForDomain(ps, intent, userId)) {
                             if (DEBUG_INSTANT) {
-                                Slog.v(TAG, "DENY instant app;"
-                                        + " pkg: " + packageName + ", status: " + status);
+                                Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName
+                                        + ", approved");
                             }
                             return false;
                         }
@@ -4475,21 +4250,6 @@
             return updateFlagsForComponent(flags, userId);
         }
 
-        // Returns a packed value as a long:
-        //
-        // high 'int'-sized word: link status: undefined/ask/never/always.
-        // low 'int'-sized word: relative priority among 'always' results.
-        public long getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
-            long result = ps.getDomainVerificationStatusForUser(userId);
-            // if none available, get the status
-            if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
-                if (ps.getIntentFilterVerificationInfo() != null) {
-                    result = ((long)ps.getIntentFilterVerificationInfo().getStatus()) << 32;
-                }
-            }
-            return result;
-        }
-
         /**
          * Checks if the request is from the system or an app that has the appropriate cross-user
          * permissions defined as follows:
@@ -5339,55 +5099,6 @@
                     params.handleIntegrityVerificationFinished();
                     break;
                 }
-                case START_INTENT_FILTER_VERIFICATIONS: {
-                    IFVerificationParams params = (IFVerificationParams) msg.obj;
-                    verifyIntentFiltersIfNeeded(params.userId, params.verifierUid, params.replacing,
-                            params.packageName, params.hasDomainUrls, params.activities);
-                    break;
-                }
-                case INTENT_FILTER_VERIFIED: {
-                    final int verificationId = msg.arg1;
-
-                    final IntentFilterVerificationState state = mIntentFilterVerificationStates.get(
-                            verificationId);
-                    if (state == null) {
-                        Slog.w(TAG, "Invalid IntentFilter verification token "
-                                + verificationId + " received");
-                        break;
-                    }
-
-                    final int userId = state.getUserId();
-
-                    if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
-                            "Processing IntentFilter verification with token:"
-                            + verificationId + " and userId:" + userId);
-
-                    final IntentFilterVerificationResponse response =
-                            (IntentFilterVerificationResponse) msg.obj;
-
-                    state.setVerifierResponse(response.callerUid, response.code);
-
-                    if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
-                            "IntentFilter verification with token:" + verificationId
-                            + " and userId:" + userId
-                            + " is settings verifier response with response code:"
-                            + response.code);
-
-                    if (response.code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
-                        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Domains failing verification: "
-                                + response.getFailedDomainsString());
-                    }
-
-                    if (state.isVerificationComplete()) {
-                        mIntentFilterVerifier.receiveVerificationResponse(verificationId);
-                    } else {
-                        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
-                                "IntentFilter verification with token:" + verificationId
-                                + " was not said to be complete");
-                    }
-
-                    break;
-                }
                 case INSTANT_APP_RESOLUTION_PHASE_TWO: {
                     InstantAppResolver.doInstantAppResolutionPhaseTwo(mContext,
                             mInstantAppResolverConnection,
@@ -5448,6 +5159,12 @@
                     }
                     break;
                 }
+                case DOMAIN_VERIFICATION: {
+                    int messageCode = msg.arg1;
+                    Object object = msg.obj;
+                    mDomainVerificationManager.runMessage(messageCode, object);
+                    break;
+                }
             }
         }
     }
@@ -5807,19 +5524,19 @@
     public void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int optional,
             @Checksum.Type int required, @Nullable List trustedInstallers,
-            @NonNull IntentSender statusReceiver, int userId) {
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
         requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
-                statusReceiver, userId, mInjector.getBackgroundExecutor(),
+                onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
                 mInjector.getBackgroundHandler());
     }
 
     private void requestChecksumsInternal(@NonNull String packageName, boolean includeSplits,
-            @Checksum.Type int optional,
-            @Checksum.Type int required, @Nullable List trustedInstallers,
-            @NonNull IntentSender statusReceiver, int userId, @NonNull Executor executor,
-            @NonNull Handler handler) {
+            @Checksum.Type int optional, @Checksum.Type int required,
+            @Nullable List trustedInstallers,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
+            @NonNull Executor executor, @NonNull Handler handler) {
         Objects.requireNonNull(packageName);
-        Objects.requireNonNull(statusReceiver);
+        Objects.requireNonNull(onChecksumsReadyListener);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(handler);
 
@@ -5855,7 +5572,7 @@
                     () -> mInjector.getIncrementalManager(),
                     () -> mPmInternal);
             ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
-                    trustedCerts, statusReceiver, injector);
+                    trustedCerts, onChecksumsReadyListener, injector);
         });
     }
 
@@ -6014,7 +5731,8 @@
     }
 
     public static PackageManagerService main(Context context, Installer installer,
-            boolean factoryTest, boolean onlyCore) {
+            @NonNull DomainVerificationService domainVerificationService, boolean factoryTest,
+            boolean onlyCore) {
         // Self-check for initial settings.
         PackageManagerServiceCompilerMapping.checkProperties();
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
@@ -6037,7 +5755,8 @@
                         lock),
                 (i, pm) -> new Settings(Environment.getDataDirectory(),
                         RuntimePermissionsPersistence.createInstance(),
-                        i.getPermissionManagerServiceInternal(), lock),
+                        i.getPermissionManagerServiceInternal(),
+                        domainVerificationService, lock),
                 (i, pm) -> AppsFilter.create(pm.mPmInternal, i),
                 (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),
                 (i, pm) -> SystemConfig.getInstance(),
@@ -6070,6 +5789,13 @@
                         i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),
                 (i, pm) -> new ModuleInfoProvider(i.getContext(), pm),
                 (i, pm) -> LegacyPermissionManagerService.create(i.getContext()),
+                (i, pm) -> domainVerificationService,
+                (i, pm) -> {
+                    HandlerThread thread = new ServiceThread(TAG,
+                            Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
+                    thread.start();
+                    return pm.new PackageHandler(thread.getLooper());
+                },
                 new DefaultSystemWrapper(),
                 LocalServices::getService,
                 context::getSystemService);
@@ -6241,6 +5967,9 @@
         mPermissionManager = injector.getPermissionManagerServiceInternal();
         mSettings = injector.getSettings();
         mUserManager = injector.getUserManagerService();
+        mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
+        mHandler = injector.getHandler();
+
         mApexManager = testParams.apexManager;
         mArtManagerService = testParams.artManagerService;
         mAvailableFeatures = testParams.availableFeatures;
@@ -6250,14 +5979,11 @@
         mDexManager = testParams.dexManager;
         mDirsToScanAsSystem = testParams.dirsToScanAsSystem;
         mFactoryTest = testParams.factoryTest;
-        mHandler = testParams.handler;
         mIncrementalManager = testParams.incrementalManager;
         mInstallerService = testParams.installerService;
         mInstantAppRegistry = testParams.instantAppRegistry;
         mInstantAppResolverConnection = testParams.instantAppResolverConnection;
         mInstantAppResolverSettingsComponent = testParams.instantAppResolverSettingsComponent;
-        mIntentFilterVerifier = testParams.intentFilterVerifier;
-        mIntentFilterVerifierComponent = testParams.intentFilterVerifierComponent;
         mIsPreNMR1Upgrade = testParams.isPreNmr1Upgrade;
         mIsPreNUpgrade = testParams.isPreNupgrade;
         mIsPreQUpgrade = testParams.isPreQupgrade;
@@ -6463,6 +6189,9 @@
         mAppInstallDir = new File(Environment.getDataDirectory(), "app");
         mAppLib32InstallDir = getAppLib32InstallDir();
 
+        mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
+        mDomainVerificationManager.setConnection(mDomainVerificationConnection);
+
         // Link up the watchers
         mPackages.registerObserver(mWatcher);
         mSharedLibraries.registerObserver(mWatcher);
@@ -6485,10 +6214,7 @@
         synchronized (mInstallLock) {
         // writer
         synchronized (mLock) {
-            HandlerThread handlerThread = new ServiceThread(TAG,
-                    Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
-            handlerThread.start();
-            mHandler = new PackageHandler(handlerThread.getLooper());
+            mHandler = injector.getHandler();
             mProcessLoggingHandler = new ProcessLoggingHandler();
             Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
 
@@ -6973,7 +6699,6 @@
             if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) {
                 for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
                     mSettings.applyDefaultPreferredAppsLPw(user.id);
-                    primeDomainVerificationsLPw(user.id);
                 }
             }
 
@@ -7080,13 +6805,18 @@
                 mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr();
                 mRequiredInstallerPackage = getRequiredInstallerLPr();
                 mRequiredUninstallerPackage = getRequiredUninstallerLPr();
-                mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
-                if (mIntentFilterVerifierComponent != null) {
-                    mIntentFilterVerifier = new IntentVerifierProxy(mContext,
-                            mIntentFilterVerifierComponent);
-                } else {
-                    mIntentFilterVerifier = null;
-                }
+                ComponentName intentFilterVerifierComponent =
+                        getIntentFilterVerifierComponentNameLPr();
+                ComponentName domainVerificationAgent =
+                        getDomainVerificationAgentComponentNameLPr();
+
+                DomainVerificationProxy domainVerificationProxy = DomainVerificationProxy.makeProxy(
+                        intentFilterVerifierComponent, domainVerificationAgent, mContext,
+                        mDomainVerificationManager, mDomainVerificationManager.getCollector(),
+                        mDomainVerificationConnection);
+
+                mDomainVerificationManager.setProxy(domainVerificationProxy);
+
                 mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr();
                 mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
                         PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
@@ -7095,8 +6825,6 @@
                 mRequiredVerifierPackage = null;
                 mRequiredInstallerPackage = null;
                 mRequiredUninstallerPackage = null;
-                mIntentFilterVerifierComponent = null;
-                mIntentFilterVerifier = null;
                 mServicesExtensionPackageName = null;
                 mSharedSystemSharedLibraryPackageName = null;
             }
@@ -7631,6 +7359,40 @@
         return null;
     }
 
+    @Nullable
+    private ComponentName getDomainVerificationAgentComponentNameLPr() {
+        Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION);
+        List<ResolveInfo> matches = queryIntentReceiversInternal(intent, null,
+                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/);
+        ResolveInfo best = null;
+        final int N = matches.size();
+        for (int i = 0; i < N; i++) {
+            final ResolveInfo cur = matches.get(i);
+            final String packageName = cur.getComponentInfo().packageName;
+            if (checkPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
+                    packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
+                Slog.w(TAG, "Domain verification agent found but does not hold permission: "
+                        + packageName);
+                continue;
+            }
+
+            if (best == null || cur.priority > best.priority) {
+                if (cur.getComponentInfo().enabled) {
+                    best = cur;
+                } else {
+                    Slog.w(TAG, "Domain verification agent found but not enabled");
+                }
+            }
+        }
+
+        if (best != null) {
+            return best.getComponentInfo().getComponentName();
+        }
+        Slog.w(TAG, "Domain verification agent not found");
+        return null;
+    }
+
     @Override
     public @Nullable ComponentName getInstantAppResolverComponent() {
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
@@ -7764,60 +7526,6 @@
         return matches.get(0).getComponentInfo().getComponentName();
     }
 
-    @GuardedBy("mLock")
-    private void primeDomainVerificationsLPw(int userId) {
-        if (DEBUG_DOMAIN_VERIFICATION) {
-            Slog.d(TAG, "Priming domain verifications in user " + userId);
-        }
-
-        SystemConfig systemConfig = mInjector.getSystemConfig();
-        ArraySet<String> packages = systemConfig.getLinkedApps();
-
-        for (String packageName : packages) {
-            AndroidPackage pkg = mPackages.get(packageName);
-            if (pkg != null) {
-                if (!pkg.isSystem()) {
-                    Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig <app-link>");
-                    continue;
-                }
-
-                ArraySet<String> domains = null;
-                for (ParsedActivity a : pkg.getActivities()) {
-                    for (ParsedIntentInfo filter : a.getIntents()) {
-                        if (hasValidDomains(filter)) {
-                            if (domains == null) {
-                                domains = new ArraySet<>();
-                            }
-                            domains.addAll(filter.getHostsList());
-                        }
-                    }
-                }
-
-                if (domains != null && domains.size() > 0) {
-                    if (DEBUG_DOMAIN_VERIFICATION) {
-                        Slog.v(TAG, "      + " + packageName);
-                    }
-                    // 'Undefined' in the global IntentFilterVerificationInfo, i.e. the usual
-                    // state w.r.t. the formal app-linkage "no verification attempted" state;
-                    // and then 'always' in the per-user state actually used for intent resolution.
-                    final IntentFilterVerificationInfo ivi;
-                    ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, domains);
-                    ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
-                    mSettings.updateIntentFilterVerificationStatusLPw(packageName,
-                            INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId);
-                } else {
-                    Slog.w(TAG, "Sysconfig <app-link> package '" + packageName
-                            + "' does not handle web links");
-                }
-            } else {
-                Slog.w(TAG, "Unknown package " + packageName + " in sysconfig <app-link>");
-            }
-        }
-
-        scheduleWritePackageRestrictionsLocked(userId);
-        scheduleWriteSettingsLocked();
-    }
-
     private boolean packageIsBrowser(String packageName, int userId) {
         List<ResolveInfo> list = queryIntentActivitiesInternal(sBrowserIntent, null,
                 PackageManager.MATCH_ALL, userId);
@@ -9241,6 +8949,7 @@
 
     @Override
     public List<String> getAllPackages() {
+        enforceSystemOrRootOrShell("getAllPackages is limited to privileged callers");
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
         synchronized (mLock) {
@@ -9663,9 +9372,8 @@
                     if (ri.activityInfo.applicationInfo.isInstantApp()) {
                         final String packageName = ri.activityInfo.packageName;
                         final PackageSetting ps = mSettings.getPackageLPr(packageName);
-                        final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
-                        final int status = (int)(packedStatus >> 32);
-                        if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
+                        if (ps != null && mDomainVerificationManager
+                                .isApprovedForDomain(ps, intent, userId)) {
                             return ri;
                         }
                     }
@@ -10157,8 +9865,7 @@
     private static class CrossProfileDomainInfo {
         /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
         ResolveInfo resolveInfo;
-        /* Best domain verification status of the activities found in the other profile */
-        int bestDomainVerificationStatus;
+        boolean wereAnyDomainsVerificationApproved;
     }
 
     private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
@@ -10244,13 +9951,6 @@
             xpDomainInfo, userId, debug);
     }
 
-    // Returns a packed value as a long:
-    //
-    // high 'int'-sized word: link status: undefined/ask/never/always.
-    // low 'int'-sized word: relative priority among 'always' results.
-    private long getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
-        return liveComputer().getDomainVerificationStatusLPr(ps, userId);
-    }
 
     private ResolveInfo querySkipCurrentProfileIntents(
             List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
@@ -13500,7 +13200,7 @@
 
         final int userId = user == null ? 0 : user.getIdentifier();
         // Modify state for the given package setting
-        commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags,
+        commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting, scanFlags,
                 (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
         if (pkgSetting.getInstantApp(userId)) {
             mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
@@ -13757,6 +13457,9 @@
             usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
             parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
         }
+
+        final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
+
         // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
         //  to avoid adding something that's unsupported due to lack of state, since it's called
         //  with null.
@@ -13780,7 +13483,8 @@
                     parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user,
                     true /*allowInstall*/, instantApp, virtualPreload,
                     UserManagerService.getInstance(), usesStaticLibraries,
-                    parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups());
+                    parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
+                    newDomainSetId);
         } else {
             // make a deep copy to avoid modifying any existing system state.
             pkgSetting = new PackageSetting(pkgSetting);
@@ -13799,7 +13503,7 @@
                     PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
                     UserManagerService.getInstance(),
                     usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
-                    parsedPackage.getMimeGroups());
+                    parsedPackage.getMimeGroups(), newDomainSetId);
         }
         if (createNewPackage && originalPkgSetting != null) {
             // This is the initial transition from the original package, so,
@@ -14644,8 +14348,8 @@
      * Adds a scanned package to the system. When this method is finished, the package will
      * be available for query, resolution, etc...
      */
-    private void commitPackageSettings(AndroidPackage pkg,
-            @Nullable AndroidPackage oldPkg, PackageSetting pkgSetting,
+    private void commitPackageSettings(@NonNull AndroidPackage pkg, @Nullable AndroidPackage oldPkg,
+            @NonNull PackageSetting pkgSetting, @Nullable PackageSetting oldPkgSetting,
             final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
         final String pkgName = pkg.getPackageName();
         if (mCustomResolverComponentName != null &&
@@ -14768,6 +14472,12 @@
             mAppsFilter.addPackage(pkgSetting, isReplace);
             mPackageProperty.addAllProperties(pkg);
 
+            if (oldPkgSetting == null || oldPkgSetting.getPkg() == null) {
+                mDomainVerificationManager.addPackage(pkgSetting);
+            } else {
+                mDomainVerificationManager.migrateState(oldPkgSetting, pkgSetting);
+            }
+
             int collectionSize = ArrayUtils.size(pkg.getInstrumentations());
             StringBuilder r = null;
             int i;
@@ -16440,77 +16150,31 @@
         return DEFAULT_INTEGRITY_VERIFY_ENABLE;
     }
 
+    @Deprecated
     @Override
-    public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains)
-            throws RemoteException {
-        mContext.enforceCallingOrSelfPermission(
-                Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
-                "Only intentfilter verification agents can verify applications");
-
-        final Message msg = mHandler.obtainMessage(INTENT_FILTER_VERIFIED);
-        final IntentFilterVerificationResponse response = new IntentFilterVerificationResponse(
-                Binder.getCallingUid(), verificationCode, failedDomains);
-        msg.arg1 = id;
-        msg.obj = response;
-        mHandler.sendMessage(msg);
+    public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) {
+        DomainVerificationProxyV1.queueLegacyVerifyResult(mContext, mDomainVerificationConnection,
+                id, verificationCode, failedDomains, Binder.getCallingUid());
     }
 
+    @Deprecated
     @Override
     public int getIntentVerificationStatus(String packageName, int userId) {
-        final int callingUid = Binder.getCallingUid();
-        if (UserHandle.getUserId(callingUid) != userId) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    "getIntentVerificationStatus" + userId);
-        }
-        if (getInstantAppPackageName(callingUid) != null) {
-            return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-        }
-        synchronized (mLock) {
-            final PackageSetting ps = mSettings.getPackageLPr(packageName);
-            if (ps == null
-                    || shouldFilterApplicationLocked(
-                    ps, callingUid, UserHandle.getUserId(callingUid))) {
-                return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-            }
-            return mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
-        }
+        return mDomainVerificationManager.getLegacyState(packageName, userId);
     }
 
+    @Deprecated
     @Override
     public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
-
-        boolean result = false;
-        synchronized (mLock) {
-            final PackageSetting ps = mSettings.getPackageLPr(packageName);
-            if (shouldFilterApplicationLocked(
-                    ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
-                return false;
-            }
-            result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId);
-        }
-        if (result) {
-            scheduleWritePackageRestrictionsLocked(userId);
-        }
-        return result;
+        mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
+        return true;
     }
 
+    @Deprecated
     @Override
     public @NonNull ParceledListSlice<IntentFilterVerificationInfo> getIntentFilterVerifications(
             String packageName) {
-        final int callingUid = Binder.getCallingUid();
-        if (getInstantAppPackageName(callingUid) != null) {
-            return ParceledListSlice.emptyList();
-        }
-        synchronized (mLock) {
-            final PackageSetting ps = mSettings.getPackageLPr(packageName);
-            if (shouldFilterApplicationLocked(ps, callingUid, UserHandle.getUserId(callingUid))) {
-                return ParceledListSlice.emptyList();
-            }
-            return new ParceledListSlice<>(mSettings.getIntentFilterVerificationsLPr(packageName));
-        }
+        return ParceledListSlice.emptyList();
     }
 
     @Override
@@ -20193,13 +19857,6 @@
                     "Failed to set up verity: " + e);
         }
 
-        if (!instantApp) {
-            startIntentFilterVerifications(args.user.getIdentifier(), replace, parsedPackage);
-        } else {
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
-            }
-        }
         final PackageFreezer freezer =
                 freezePackageForInstall(pkgName, installFlags, "installPackageLI");
         boolean shouldCloseFreezerBeforeReturn = true;
@@ -20533,190 +20190,6 @@
         }
     }
 
-    private void startIntentFilterVerifications(int userId, boolean replacing, AndroidPackage pkg) {
-        if (mIntentFilterVerifierComponent == null) {
-            Slog.w(TAG, "No IntentFilter verification will not be done as "
-                    + "there is no IntentFilterVerifier available!");
-            return;
-        }
-
-        final int verifierUid = getPackageUid(
-                mIntentFilterVerifierComponent.getPackageName(),
-                MATCH_DEBUG_TRIAGED_MISSING,
-                (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
-
-        Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
-        msg.obj = new IFVerificationParams(
-                pkg.getPackageName(),
-                pkg.isHasDomainUrls(),
-                pkg.getActivities(),
-                replacing,
-                userId,
-                verifierUid
-        );
-        mHandler.sendMessage(msg);
-    }
-
-    private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing,
-            String packageName,
-            boolean hasDomainUrls,
-            List<ParsedActivity> activities) {
-        int size = activities.size();
-        if (size == 0) {
-            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
-                    "No activity, so no need to verify any IntentFilter!");
-            return;
-        }
-
-        if (!hasDomainUrls) {
-            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
-                    "No domain URLs, so no need to verify any IntentFilter!");
-            return;
-        }
-
-        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Checking for userId:" + userId
-                + " if any IntentFilter from the " + size
-                + " Activities needs verification ...");
-
-        int count = 0;
-        boolean handlesWebUris = false;
-        ArraySet<String> domains = new ArraySet<>();
-        final boolean previouslyVerified;
-        boolean hostSetExpanded = false;
-        boolean needToRunVerify = false;
-        synchronized (mLock) {
-            // If this is a new install and we see that we've already run verification for this
-            // package, we have nothing to do: it means the state was restored from backup.
-            IntentFilterVerificationInfo ivi =
-                    mSettings.getIntentFilterVerificationLPr(packageName);
-            previouslyVerified = (ivi != null);
-            if (!replacing && previouslyVerified) {
-                if (DEBUG_DOMAIN_VERIFICATION) {
-                    Slog.i(TAG, "Package " + packageName + " already verified: status="
-                            + ivi.getStatusString());
-                }
-                return;
-            }
-
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.i(TAG, "    Previous verified hosts: "
-                        + (ivi == null ? "[none]" : ivi.getDomainsString()));
-            }
-
-            // If any filters need to be verified, then all need to be.  In addition, we need to
-            // know whether an updating app has any web navigation intent filters, to re-
-            // examine handling policy even if not re-verifying.
-            final boolean needsVerification = needsNetworkVerificationLPr(packageName);
-            for (ParsedActivity a : activities) {
-                for (ParsedIntentInfo filter : a.getIntents()) {
-                    if (filter.handlesWebUris(true)) {
-                        handlesWebUris = true;
-                    }
-                    if (needsVerification && filter.needsVerification()) {
-                        if (DEBUG_DOMAIN_VERIFICATION) {
-                            Slog.d(TAG, "autoVerify requested, processing all filters");
-                        }
-                        needToRunVerify = true;
-                        // It's safe to break out here because filter.needsVerification()
-                        // can only be true if filter.handlesWebUris(true) returned true, so
-                        // we've already noted that.
-                        break;
-                    }
-                }
-            }
-
-            // Compare the new set of recognized hosts if the app is either requesting
-            // autoVerify or has previously used autoVerify but no longer does.
-            if (needToRunVerify || previouslyVerified) {
-                final int verificationId = mIntentFilterVerificationToken++;
-                for (ParsedActivity a : activities) {
-                    for (ParsedIntentInfo filter : a.getIntents()) {
-                        // Run verification against hosts mentioned in any web-nav intent filter,
-                        // even if the filter matches non-web schemes as well
-                        if (filter.handlesWebUris(false /*onlyWebSchemes*/)) {
-                            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
-                                    "Verification needed for IntentFilter:" + filter.toString());
-                            mIntentFilterVerifier.addOneIntentFilterVerification(
-                                    verifierUid, userId, verificationId, filter, packageName);
-                            domains.addAll(filter.getHostsList());
-                            count++;
-                        }
-                    }
-                }
-            }
-
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.i(TAG, "    Update published hosts: " + domains.toString());
-            }
-
-            // If we've previously verified this same host set (or a subset), we can trust that
-            // a current ALWAYS policy is still applicable.  If this is the case, we're done.
-            // (If we aren't in ALWAYS, we want to reverify to allow for apps that had failing
-            // hosts in their intent filters, then pushed a new apk that removed them and now
-            // passes.)
-            //
-            // Cases:
-            //   + still autoVerify (needToRunVerify):
-            //      - preserve current state if all of: unexpanded, in always
-            //      - otherwise rerun as usual (fall through)
-            //   + no longer autoVerify (alreadyVerified && !needToRunVerify)
-            //      - wipe verification history always
-            //      - preserve current state if all of: unexpanded, in always
-            hostSetExpanded = !previouslyVerified
-                    || (ivi != null && !ivi.getDomains().containsAll(domains));
-            final int currentPolicy =
-                    mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
-            final boolean keepCurState = !hostSetExpanded
-                    && currentPolicy == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-
-            if (needToRunVerify && keepCurState) {
-                if (DEBUG_DOMAIN_VERIFICATION) {
-                    Slog.i(TAG, "Host set not expanding + ALWAYS -> no need to reverify");
-                }
-                ivi.setDomains(domains);
-                scheduleWriteSettingsLocked();
-                return;
-            } else if (previouslyVerified && !needToRunVerify) {
-                // Prior autoVerify state but not requesting it now.  Clear autoVerify history,
-                // and preserve the always policy iff the host set is not expanding.
-                clearIntentFilterVerificationsLPw(packageName, userId, !keepCurState);
-                return;
-            }
-        }
-
-        if (needToRunVerify && count > 0) {
-            // app requested autoVerify and has at least one matching intent filter
-            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count
-                    + " IntentFilter verification" + (count > 1 ? "s" : "")
-                    +  " for userId:" + userId);
-            mIntentFilterVerifier.startVerifications(userId);
-        } else {
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.d(TAG, "No web filters or no new host policy for " + packageName);
-            }
-        }
-    }
-
-    @GuardedBy("mLock")
-    private boolean needsNetworkVerificationLPr(String packageName) {
-        IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(
-                packageName);
-        if (ivi == null) {
-            return true;
-        }
-        int status = ivi.getStatus();
-        switch (status) {
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
-                return true;
-
-            default:
-                // Nothing to do
-                return false;
-        }
-    }
-
     private static boolean isExternal(PackageSetting ps) {
         return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
     }
@@ -21386,7 +20859,7 @@
             if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
                 final SparseBooleanArray changedUsers = new SparseBooleanArray();
                 synchronized (mLock) {
-                    clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true);
+                    mDomainVerificationManager.clearPackage(deletedPs.name);
                     clearDefaultBrowserIfNeeded(packageName);
                     mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName);
                     mAppsFilter.removePackage(getPackageSetting(packageName));
@@ -21913,8 +21386,6 @@
                     null /*lastDisableAppCaller*/,
                     null /*enabledComponents*/,
                     null /*disabledComponents*/,
-                    ps.readUserState(nextUserId).domainVerificationStatus,
-                    0 /*linkGeneration*/,
                     PackageManager.INSTALL_REASON_UNKNOWN,
                     PackageManager.UNINSTALL_REASON_UNKNOWN,
                     null /*harmfulAppWarning*/);
@@ -22464,40 +21935,6 @@
         mSettings.clearPackagePreferredActivities(packageName, outUserChanged, userId);
     }
 
-    /** This method takes a specific user id as well as UserHandle.USER_ALL. */
-    @GuardedBy("mLock")
-    private void clearIntentFilterVerificationsLPw(int userId) {
-        final int packageCount = mPackages.size();
-        for (int i = 0; i < packageCount; i++) {
-            AndroidPackage pkg = mPackages.valueAt(i);
-            clearIntentFilterVerificationsLPw(pkg.getPackageName(), userId, true);
-        }
-    }
-
-    /** This method takes a specific user id as well as UserHandle.USER_ALL. */
-    @GuardedBy("mLock")
-    void clearIntentFilterVerificationsLPw(String packageName, int userId,
-            boolean alsoResetStatus) {
-        if (SystemConfig.getInstance().getLinkedApps().contains(packageName)) {
-            // Nope, need to preserve the system configuration approval for this app
-            return;
-        }
-
-        if (userId == UserHandle.USER_ALL) {
-            if (mSettings.removeIntentFilterVerificationLPw(packageName,
-                    mUserManager.getUserIds())) {
-                for (int oneUserId : mUserManager.getUserIds()) {
-                    scheduleWritePackageRestrictionsLocked(oneUserId);
-                }
-            }
-        } else {
-            if (mSettings.removeIntentFilterVerificationLPw(packageName, userId,
-                    alsoResetStatus)) {
-                scheduleWritePackageRestrictionsLocked(userId);
-            }
-        }
-    }
-
     /** Clears state for all users, and touches intent filter verification policy */
     void clearDefaultBrowserIfNeeded(String packageName) {
         for (int oneUserId : mUserManager.getUserIds()) {
@@ -22553,8 +21990,7 @@
             }
             synchronized (mLock) {
                 mSettings.applyDefaultPreferredAppsLPw(userId);
-                clearIntentFilterVerificationsLPw(userId);
-                primeDomainVerificationsLPw(userId);
+                mDomainVerificationManager.clearUser(userId);
                 final int numPackages = mPackages.size();
                 for (int i = 0; i < numPackages; i++) {
                     final AndroidPackage pkg = mPackages.valueAt(i);
@@ -22816,28 +22252,8 @@
             throw new SecurityException("Only the system may call getIntentFilterVerificationBackup()");
         }
 
-        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
-        try {
-            final TypedXmlSerializer serializer = Xml.newFastSerializer();
-            serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
-            serializer.startDocument(null, true);
-            serializer.startTag(null, TAG_INTENT_FILTER_VERIFICATION);
-
-            synchronized (mLock) {
-                mSettings.writeAllDomainVerificationsLPr(serializer, userId);
-            }
-
-            serializer.endTag(null, TAG_INTENT_FILTER_VERIFICATION);
-            serializer.endDocument();
-            serializer.flush();
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Unable to write default apps for backup", e);
-            }
-            return null;
-        }
-
-        return dataStream.toByteArray();
+        // TODO(b/170746586)
+        return null;
     }
 
     @Override
@@ -22845,22 +22261,7 @@
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
             throw new SecurityException("Only the system may call restorePreferredActivities()");
         }
-
-        try {
-            final TypedXmlPullParser parser = Xml.newFastPullParser();
-            parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
-            restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION,
-                    (parser1, userId1) -> {
-                        synchronized (mLock) {
-                            mSettings.readAllDomainVerificationsLPr(parser1, userId1);
-                            writeSettingsLPrTEMP();
-                        }
-                    });
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
-            }
-        }
+        // TODO(b/170746586)
     }
 
     @Override
@@ -24113,8 +23514,8 @@
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, String[] args, ShellCallback callback,
             ResultReceiver resultReceiver) {
-        (new PackageManagerShellCommand(this, mContext)).exec(
-                this, in, out, err, args, callback, resultReceiver);
+        (new PackageManagerShellCommand(this, mContext,mDomainVerificationManager.getShell()))
+                .exec(this, in, out, err, args, callback, resultReceiver);
     }
 
     @SuppressWarnings("resource")
@@ -24296,9 +23697,8 @@
                 dumpState.setDump(DumpState.DUMP_MESSAGES);
             } else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_VERIFIERS);
-            } else if ("i".equals(cmd) || "ifv".equals(cmd)
-                    || "intent-filter-verifiers".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_INTENT_FILTER_VERIFIERS);
+            } else if ("dv".equals(cmd) || "domain-verifier".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_DOMAIN_VERIFIER);
             } else if ("version".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_VERSION);
             } else if ("k".equals(cmd) || "keysets".equals(cmd)) {
@@ -24392,14 +23792,16 @@
                 }
             }
 
-            if (dumpState.isDumping(DumpState.DUMP_INTENT_FILTER_VERIFIERS) &&
+            if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) &&
                     packageName == null) {
-                if (mIntentFilterVerifierComponent != null) {
-                    String verifierPackageName = mIntentFilterVerifierComponent.getPackageName();
+                DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
+                ComponentName verifierComponent = proxy.getComponentName();
+                if (verifierComponent != null) {
+                    String verifierPackageName = verifierComponent.getPackageName();
                     if (!checkin) {
                         if (dumpState.onTitlePrinted())
                             pw.println();
-                        pw.println("Intent Filter Verifier:");
+                        pw.println("Domain Verifier:");
                         pw.print("  Using: ");
                         pw.print(verifierPackageName);
                         pw.print(" (uid=");
@@ -24407,14 +23809,14 @@
                                 UserHandle.USER_SYSTEM));
                         pw.println(")");
                     } else if (verifierPackageName != null) {
-                        pw.print("ifv,"); pw.print(verifierPackageName);
+                        pw.print("dv,"); pw.print(verifierPackageName);
                         pw.print(",");
                         pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
                                 UserHandle.USER_SYSTEM));
                     }
                 } else {
                     pw.println();
-                    pw.println("No Intent Filter Verifier available!");
+                    pw.println("No Domain Verifier available!");
                 }
             }
 
@@ -24531,63 +23933,20 @@
                 }
             }
 
-            if (!checkin
-                    && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)
-                    && packageName == null) {
-                pw.println();
-                int count = mSettings.getPackagesLocked().size();
-                if (count == 0) {
-                    pw.println("No applications!");
-                    pw.println();
-                } else {
-                    final String prefix = "  ";
-                    Collection<PackageSetting> allPackageSettings =
-                            mSettings.getPackagesLocked().values();
-                    if (allPackageSettings.size() == 0) {
-                        pw.println("No domain preferred apps!");
-                        pw.println();
-                    } else {
-                        pw.println("App verification status:");
-                        pw.println();
-                        count = 0;
-                        for (PackageSetting ps : allPackageSettings) {
-                            IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
-                            if (ivi == null || ivi.getPackageName() == null) continue;
-                            pw.println(prefix + "Package: " + ivi.getPackageName());
-                            pw.println(prefix + "Domains: " + ivi.getDomainsString());
-                            pw.println(prefix + "Status:  " + ivi.getStatusString());
-                            pw.println();
-                            count++;
-                        }
-                        if (count == 0) {
-                            pw.println(prefix + "No app verification established.");
-                            pw.println();
-                        }
-                        for (int userId : mUserManager.getUserIds()) {
-                            pw.println("App linkages for user " + userId + ":");
-                            pw.println();
-                            count = 0;
-                            for (PackageSetting ps : allPackageSettings) {
-                                final long status = ps.getDomainVerificationStatusForUser(userId);
-                                if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
-                                        && !DEBUG_DOMAIN_VERIFICATION) {
-                                    continue;
-                                }
-                                pw.println(prefix + "Package: " + ps.name);
-                                pw.println(prefix + "Domains: " + dumpDomainString(ps.name));
-                                String statusStr = IntentFilterVerificationInfo.
-                                        getStatusStringFromValue(status);
-                                pw.println(prefix + "Status:  " + statusStr);
-                                pw.println();
-                                count++;
-                            }
-                            if (count == 0) {
-                                pw.println(prefix + "No configured app linkages.");
-                                pw.println();
-                            }
-                        }
-                    }
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+                android.util.IndentingPrintWriter writer =
+                        new android.util.IndentingPrintWriter(pw);
+                if (dumpState.onTitlePrinted()) pw.println();
+
+                writer.println("Domain verification status:");
+                writer.increaseIndent();
+                try {
+                    mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL);
+                } catch (PackageManager.NameNotFoundException e) {
+                    pw.println("Failure printing domain verification information");
+                    Slog.e(TAG, "Failure printing domain verification information", e);
                 }
+                writer.decreaseIndent();
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
@@ -24776,8 +24135,10 @@
                             UserHandle.USER_SYSTEM));
             proto.end(requiredVerifierPackageToken);
 
-            if (mIntentFilterVerifierComponent != null) {
-                String verifierPackageName = mIntentFilterVerifierComponent.getPackageName();
+            DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
+            ComponentName verifierComponent = proxy.getComponentName();
+            if (verifierComponent != null) {
+                String verifierPackageName = verifierComponent.getPackageName();
                 final long verifierPackageToken =
                         proto.start(PackageServiceDumpProto.VERIFIER_PACKAGE);
                 proto.write(PackageServiceDumpProto.PackageShortProto.NAME, verifierPackageName);
@@ -24902,35 +24263,6 @@
         }
     }
 
-    private String dumpDomainString(String packageName) {
-        List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName)
-                .getList();
-        List<IntentFilter> filters = getAllIntentFilters(packageName).getList();
-
-        ArraySet<String> result = new ArraySet<>();
-        if (iviList.size() > 0) {
-            for (IntentFilterVerificationInfo ivi : iviList) {
-                result.addAll(ivi.getDomains());
-            }
-        }
-        if (filters != null && filters.size() > 0) {
-            for (IntentFilter filter : filters) {
-                if (filter.hasCategory(Intent.CATEGORY_BROWSABLE)
-                        && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
-                                filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
-                    result.addAll(filter.getHostsList());
-                }
-            }
-        }
-
-        StringBuilder sb = new StringBuilder(result.size() * 16);
-        for (String domain : result) {
-            if (sb.length() > 0) sb.append(" ");
-            sb.append(domain);
-        }
-        return sb.toString();
-    }
-
     // ------- apps on sdcard specific code -------
     static final boolean DEBUG_SD_INSTALL = false;
 
@@ -26126,7 +25458,6 @@
         synchronized (mLock) {
             scheduleWritePackageRestrictionsLocked(userId);
             scheduleWritePackageListLocked(userId);
-            primeDomainVerificationsLPw(userId);
             mAppsFilter.onUsersChanged();
         }
     }
@@ -27781,12 +27112,14 @@
             ps.setStatesOnCrashOrAnr();
         }
 
+        @Override
         public void requestChecksums(@NonNull String packageName, boolean includeSplits,
                 @Checksum.Type int optional, @Checksum.Type int required,
-                @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+                @Nullable List trustedInstallers,
+                @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
                 @NonNull Executor executor, @NonNull Handler handler) {
             requestChecksumsInternal(packageName, includeSplits, optional, required,
-                    trustedInstallers, statusReceiver, userId, executor, handler);
+                    trustedInstallers, onChecksumsReadyListener, userId, executor, handler);
         }
 
         @Override
@@ -28426,6 +27759,11 @@
                 duration);
         return bOptions;
     }
+
+    @NonNull
+    public DomainVerificationService.Connection getDomainVerificationConnection() {
+        return mDomainVerificationConnection;
+    }
 }
 
 interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 212edf6..b5765b5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -17,13 +17,9 @@
 package com.android.server.pm;
 
 import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
 
 import android.accounts.IAccountManager;
+import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -108,6 +104,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
+import com.android.server.pm.verify.domain.DomainVerificationShell;
 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
 
 import dalvik.system.DexFile;
@@ -149,6 +146,7 @@
     final LegacyPermissionManagerInternal mLegacyPermissionManager;
     final PermissionManager mPermissionManager;
     final Context mContext;
+    final DomainVerificationShell mDomainVerificationShell;
     final private WeakHashMap<String, Resources> mResourceCache =
             new WeakHashMap<String, Resources>();
     int mTargetUser;
@@ -158,11 +156,13 @@
 
     private static final SecureRandom RANDOM = new SecureRandom();
 
-    PackageManagerShellCommand(PackageManagerService service, Context context) {
+    PackageManagerShellCommand(@NonNull PackageManagerService service,
+            @NonNull Context context, @NonNull DomainVerificationShell domainVerificationShell) {
         mInterface = service;
         mLegacyPermissionManager = LocalServices.getService(LegacyPermissionManagerInternal.class);
         mPermissionManager = context.getSystemService(PermissionManager.class);
         mContext = context;
+        mDomainVerificationShell = domainVerificationShell;
     }
 
     @Override
@@ -267,10 +267,6 @@
                     return runGetPrivappDenyPermissions();
                 case "get-oem-permissions":
                     return runGetOemPermissions();
-                case "set-app-link":
-                    return runSetAppLink();
-                case "get-app-link":
-                    return runGetAppLink();
                 case "trim-caches":
                     return runTrimCaches();
                 case "create-user":
@@ -309,6 +305,12 @@
                 case "bypass-staged-installer-check":
                     return runBypassStagedInstallerCheck();
                 default: {
+                    Boolean domainVerificationResult =
+                            mDomainVerificationShell.runCommand(this, cmd);
+                    if (domainVerificationResult != null) {
+                        return domainVerificationResult ? 0 : 1;
+                    }
+
                     String nextArg = getNextArg();
                     if (nextArg == null) {
                         if (cmd.equalsIgnoreCase("-l")) {
@@ -2427,134 +2429,6 @@
         return 0;
     }
 
-    private String linkStateToString(int state) {
-        switch (state) {
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED: return "undefined";
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: return "ask";
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: return "always";
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER: return "never";
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK : return "always ask";
-        }
-        return "Unknown link state: " + state;
-    }
-
-    // pm set-app-link [--user USER_ID] PACKAGE {always|ask|always-ask|never|undefined}
-    private int runSetAppLink() throws RemoteException {
-        int userId = UserHandle.USER_SYSTEM;
-
-        String opt;
-        while ((opt = getNextOption()) != null) {
-            if (opt.equals("--user")) {
-                userId = UserHandle.parseUserArg(getNextArgRequired());
-            } else {
-                getErrPrintWriter().println("Error: unknown option: " + opt);
-                return 1;
-            }
-        }
-
-        // Package name to act on; required
-        final String pkg = getNextArg();
-        if (pkg == null) {
-            getErrPrintWriter().println("Error: no package specified.");
-            return 1;
-        }
-
-        // State to apply; {always|ask|never|undefined}, required
-        final String modeString = getNextArg();
-        if (modeString == null) {
-            getErrPrintWriter().println("Error: no app link state specified.");
-            return 1;
-        }
-
-        final int newMode;
-        switch (modeString.toLowerCase()) {
-            case "undefined":
-                newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-                break;
-
-            case "always":
-                newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-                break;
-
-            case "ask":
-                newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
-                break;
-
-            case "always-ask":
-                newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
-                break;
-
-            case "never":
-                newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-                break;
-
-            default:
-                getErrPrintWriter().println("Error: unknown app link state '" + modeString + "'");
-                return 1;
-        }
-
-        final int translatedUserId =
-                translateUserId(userId, UserHandle.USER_NULL, "runSetAppLink");
-        final PackageInfo info = mInterface.getPackageInfo(pkg, 0, translatedUserId);
-        if (info == null) {
-            getErrPrintWriter().println("Error: package " + pkg + " not found.");
-            return 1;
-        }
-
-        if ((info.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) {
-            getErrPrintWriter().println("Error: package " + pkg + " does not handle web links.");
-            return 1;
-        }
-
-        if (!mInterface.updateIntentVerificationStatus(pkg, newMode, translatedUserId)) {
-            getErrPrintWriter().println("Error: unable to update app link status for " + pkg);
-            return 1;
-        }
-
-        return 0;
-    }
-
-    // pm get-app-link [--user USER_ID] PACKAGE
-    private int runGetAppLink() throws RemoteException {
-        int userId = UserHandle.USER_SYSTEM;
-
-        String opt;
-        while ((opt = getNextOption()) != null) {
-            if (opt.equals("--user")) {
-                userId = UserHandle.parseUserArg(getNextArgRequired());
-            } else {
-                getErrPrintWriter().println("Error: unknown option: " + opt);
-                return 1;
-            }
-        }
-
-        // Package name to act on; required
-        final String pkg = getNextArg();
-        if (pkg == null) {
-            getErrPrintWriter().println("Error: no package specified.");
-            return 1;
-        }
-
-        final int translatedUserId =
-                translateUserId(userId, UserHandle.USER_NULL, "runGetAppLink");
-        final PackageInfo info = mInterface.getPackageInfo(pkg, 0, translatedUserId);
-        if (info == null) {
-            getErrPrintWriter().println("Error: package " + pkg + " not found.");
-            return 1;
-        }
-
-        if ((info.applicationInfo.privateFlags
-                & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) {
-            getErrPrintWriter().println("Error: package " + pkg + " does not handle web links.");
-            return 1;
-        }
-
-        getOutPrintWriter().println(linkStateToString(
-                mInterface.getIntentVerificationStatus(pkg, translatedUserId)));
-
-        return 0;
-    }
-
     private int runTrimCaches() throws RemoteException {
         String size = getNextArg();
         if (size == null) {
@@ -3915,6 +3789,8 @@
         pw.println("      --enable: turn on debug logging (default)");
         pw.println("      --disable: turn off debug logging");
         pw.println("");
+        mDomainVerificationShell.printHelp(pw);
+        pw.println("");
         Intent.printIntentArgsHelp(pw , "");
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index ade087b..69e84b5 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -39,6 +39,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 
 /**
  * Settings data for a particular package we know about.
@@ -99,18 +100,23 @@
     @NonNull
     private PackageStateUnserialized pkgState = new PackageStateUnserialized();
 
+    @NonNull
+    private UUID mDomainSetId;
+
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public PackageSetting(String name, String realName, @NonNull File codePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
             long pVersionCode, int pkgFlags, int privateFlags,
             int sharedUserId, String[] usesStaticLibraries,
-            long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups) {
+            long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups,
+            @NonNull UUID domainSetId) {
         super(name, realName, codePath, legacyNativeLibraryPathString,
                 primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
                 pVersionCode, pkgFlags, privateFlags,
                 usesStaticLibraries, usesStaticLibrariesVersions);
         this.sharedUserId = sharedUserId;
+        mDomainSetId = domainSetId;
         copyMimeGroups(mimeGroups);
     }
 
@@ -168,6 +174,7 @@
         sharedUser = orig.sharedUser;
         sharedUserId = orig.sharedUserId;
         copyMimeGroups(orig.mimeGroups);
+        mDomainSetId = orig.getDomainSetId();
     }
 
     private void copyMimeGroups(@Nullable Map<String, ArraySet<String>> newMimeGroups) {
@@ -374,6 +381,7 @@
         pkg = other.pkg;
         sharedUserId = other.sharedUserId;
         sharedUser = other.sharedUser;
+        mDomainSetId = other.mDomainSetId;
 
         Set<String> mimeGroupNames = other.mimeGroups != null ? other.mimeGroups.keySet() : null;
         updateMimeGroups(mimeGroupNames);
@@ -385,4 +393,14 @@
     public PackageStateUnserialized getPkgState() {
         return pkgState;
     }
+
+    @NonNull
+    public UUID getDomainSetId() {
+        return mDomainSetId;
+    }
+
+    public PackageSetting setDomainSetId(@NonNull UUID domainSetId) {
+        mDomainSetId = domainSetId;
+        return this;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 67bd82b4..8aa553d 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -42,6 +42,8 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationService;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.File;
@@ -131,8 +133,6 @@
     /** Whether or not an update is available. Ostensibly only for instant apps. */
     boolean updateAvailable;
 
-    IntentFilterVerificationInfo verificationInfo;
-
     boolean forceQueryableOverride;
 
     @NonNull
@@ -258,7 +258,6 @@
         for (int i = 0; i < orig.mUserState.size(); i++) {
             mUserState.put(orig.mUserState.keyAt(i), orig.mUserState.valueAt(i));
         }
-        verificationInfo = orig.verificationInfo;
         versionCode = orig.versionCode;
         volumeUuid = orig.volumeUuid;
         categoryHint = orig.categoryHint;
@@ -350,9 +349,12 @@
         return readUserState(userId).getSharedLibraryOverlayPaths();
     }
 
-    /** Only use for testing. Do NOT use in production code. */
+    /**
+     * Only use for testing. Do NOT use in production code.
+     */
     @VisibleForTesting
-    SparseArray<PackageUserState> getUserState() {
+    @Deprecated
+    public SparseArray<PackageUserState> getUserState() {
         return mUserState;
     }
 
@@ -496,8 +498,7 @@
             ArrayMap<String, PackageUserState.SuspendParams> suspendParams, boolean instantApp,
             boolean virtualPreload, String lastDisableAppCaller,
             ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
-            int domainVerifState, int linkGeneration, int installReason, int uninstallReason,
-            String harmfulAppWarning) {
+            int installReason, int uninstallReason, String harmfulAppWarning) {
         PackageUserState state = modifyUserState(userId);
         state.ceDataInode = ceDataInode;
         state.enabled = enabled;
@@ -511,8 +512,6 @@
         state.lastDisableAppCaller = lastDisableAppCaller;
         state.enabledComponents = enabledComponents;
         state.disabledComponents = disabledComponents;
-        state.domainVerificationStatus = domainVerifState;
-        state.appLinkGeneration = linkGeneration;
         state.installReason = installReason;
         state.uninstallReason = uninstallReason;
         state.instantApp = instantApp;
@@ -528,7 +527,6 @@
                 otherState.instantApp,
                 otherState.virtualPreload, otherState.lastDisableAppCaller,
                 otherState.enabledComponents, otherState.disabledComponents,
-                otherState.domainVerificationStatus, otherState.appLinkGeneration,
                 otherState.installReason, otherState.uninstallReason, otherState.harmfulAppWarning);
     }
 
@@ -644,40 +642,6 @@
         return excludedUserIds;
     }
 
-    IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
-        return verificationInfo;
-    }
-
-    void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) {
-        verificationInfo = info;
-        onChanged();
-    }
-
-    // Returns a packed value as a long:
-    //
-    // high 'int'-sized word: link status: undefined/ask/never/always.
-    // low 'int'-sized word: relative priority among 'always' results.
-    long getDomainVerificationStatusForUser(int userId) {
-        PackageUserState state = readUserState(userId);
-        long result = (long) state.appLinkGeneration;
-        result |= ((long) state.domainVerificationStatus) << 32;
-        return result;
-    }
-
-    void setDomainVerificationStatusForUser(final int status, int generation, int userId) {
-        PackageUserState state = modifyUserState(userId);
-        state.domainVerificationStatus = status;
-        if (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
-            state.appLinkGeneration = generation;
-            onChanged();
-        }
-    }
-
-    void clearDomainVerificationStatusForUser(int userId) {
-        modifyUserState(userId).domainVerificationStatus =
-                PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-    }
-
     protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) {
         int count = mUserState.size();
         for (int i = 0; i < count; i++) {
@@ -845,7 +809,6 @@
         this.volumeUuid = other.volumeUuid;
         this.categoryHint = other.categoryHint;
         this.updateAvailable = other.updateAvailable;
-        this.verificationInfo = other.verificationInfo;
         this.forceQueryableOverride = other.forceQueryableOverride;
         this.incrementalStates = other.incrementalStates;
 
diff --git a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
index ce77c91..8c5084a 100644
--- a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
+++ b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
@@ -16,22 +16,16 @@
 
 package com.android.server.pm;
 
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
-
 import android.app.admin.SecurityLog;
 import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
 import android.content.pm.ApkChecksum;
 import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.PackageManagerInternal;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerExecutor;
-import android.os.IBinder;
-import android.os.Parcelable;
+import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -39,7 +33,6 @@
 import com.android.internal.os.BackgroundThread;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -109,26 +102,23 @@
         // Capturing local loggingInfo to still log even if hash was invalidated.
         try {
             pmi.requestChecksums(packageName, false, 0, CHECKSUM_TYPE, null,
-                    new IntentSender((IIntentSender) new IIntentSender.Stub() {
+                    new IOnChecksumsReadyListener.Stub() {
                         @Override
-                        public void send(int code, Intent intent, String resolvedType,
-                                IBinder allowlistToken, IIntentReceiver finishedReceiver,
-                                String requiredPermission, Bundle options) {
-                            processChecksums(loggingInfo, intent);
+                        public void onChecksumsReady(List<ApkChecksum> checksums)
+                                throws RemoteException {
+                            processChecksums(loggingInfo, checksums);
                         }
-                    }), context.getUserId(), mExecutor, this);
+                    }, context.getUserId(),
+                    mExecutor, this);
         } catch (Throwable t) {
             Slog.e(TAG, "requestChecksums() failed", t);
             enqueueProcessChecksum(loggingInfo, null);
         }
     }
 
-    void processChecksums(final LoggingInfo loggingInfo, Intent intent) {
-        Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS);
-        ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
-                ApkChecksum[].class);
-
-        for (ApkChecksum checksum : checksums) {
+    void processChecksums(final LoggingInfo loggingInfo, List<ApkChecksum> checksums) {
+        for (int i = 0, size = checksums.size(); i < size; ++i) {
+            ApkChecksum checksum = checksums.get(i);
             if (checksum.getType() == CHECKSUM_TYPE) {
                 processChecksum(loggingInfo, checksum.getValue());
                 return;
diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java
index ea61ca4..e5a70c3 100644
--- a/services/core/java/com/android/server/pm/RestrictionsSet.java
+++ b/services/core/java/com/android/server/pm/RestrictionsSet.java
@@ -26,6 +26,7 @@
 import android.util.TypedXmlSerializer;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.BundleUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -78,7 +79,7 @@
         if (!changed) {
             return false;
         }
-        if (!UserRestrictionsUtils.isEmpty(restrictions)) {
+        if (!BundleUtils.isEmpty(restrictions)) {
             mUserRestrictions.put(userId, restrictions);
         } else {
             mUserRestrictions.delete(userId);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 349d556..fb033e6 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -21,15 +21,12 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_USER_TYPE;
 import static android.os.Process.PACKAGE_INFO_GID;
 import static android.os.Process.SYSTEM_UID;
 
-import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
 import android.annotation.NonNull;
@@ -108,6 +105,9 @@
 import com.android.server.LocalServices;
 import com.android.server.backup.PreferredActivityBackupHelper;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationPersistence;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -131,6 +131,7 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedWriter;
 import java.io.File;
@@ -147,7 +148,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
@@ -155,6 +155,7 @@
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
 
 /**
  * Holds information about dynamic settings.
@@ -283,9 +284,9 @@
             "persistent-preferred-activities";
     static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
             "crossProfile-intent-filters";
-    private static final String TAG_DOMAIN_VERIFICATION = "domain-verification";
+    public static final String TAG_DOMAIN_VERIFICATION = "domain-verification";
     private static final String TAG_DEFAULT_APPS = "default-apps";
-    private static final String TAG_ALL_INTENT_FILTER_VERIFICATION =
+    public static final String TAG_ALL_INTENT_FILTER_VERIFICATION =
             "all-intent-filter-verifications";
     private static final String TAG_DEFAULT_BROWSER = "default-browser";
     private static final String TAG_DEFAULT_DIALER = "default-dialer";
@@ -390,12 +391,6 @@
     private final WatchedSparseArray<ArraySet<String>> mBlockUninstallPackages =
             new WatchedSparseArray<>();
 
-    // Set of restored intent-filter verification states
-    @Watched
-    private final WatchedArrayMap<String, IntentFilterVerificationInfo>
-            mRestoredIntentFilterVerifications =
-            new WatchedArrayMap<String, IntentFilterVerificationInfo>();
-
     private static final class KernelPackageState {
         int appId;
         int[] excludedUserIds;
@@ -487,7 +482,10 @@
     @Watched
     final WatchedSparseArray<String> mDefaultBrowserApp = new WatchedSparseArray<String>();
 
+    // TODO(b/161161364): This seems unused, and is probably not relevant in the new API, but should
+    //  verify.
     // App-link priority tracking, per-user
+    @NonNull
     @Watched
     final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray();
 
@@ -512,6 +510,8 @@
 
     private final LegacyPermissionDataProvider mPermissionDataProvider;
 
+    private final DomainVerificationManagerInternal mDomainVerificationManager;
+
     /**
      * The observer that watches for changes from array members
      */
@@ -538,13 +538,12 @@
         mStoppedPackagesFilename = null;
         mBackupStoppedPackagesFilename = null;
         mKernelMappingFilename = null;
-
+        mDomainVerificationManager = null;
         mPackages.registerObserver(mObserver);
         mInstallerPackages.registerObserver(mObserver);
         mKernelMapping.registerObserver(mObserver);
         mDisabledSysPackages.registerObserver(mObserver);
         mBlockUninstallPackages.registerObserver(mObserver);
-        mRestoredIntentFilterVerifications.registerObserver(mObserver);
         mVersion.registerObserver(mObserver);
         mPreferredActivities.registerObserver(mObserver);
         mPersistentPreferredActivities.registerObserver(mObserver);
@@ -554,13 +553,14 @@
         mOtherAppIds.registerObserver(mObserver);
         mRenamedPackages.registerObserver(mObserver);
         mDefaultBrowserApp.registerObserver(mObserver);
-        mNextAppLinkGeneration.registerObserver(mObserver);
 
         Watchable.verifyWatchedAttributes(this, mObserver);
     }
 
     Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
-            LegacyPermissionDataProvider permissionDataProvider, Object lock) {
+            LegacyPermissionDataProvider permissionDataProvider,
+            @NonNull DomainVerificationManagerInternal domainVerificationManager,
+            @NonNull Object lock)  {
         mLock = lock;
         mAppIds = new WatchedArrayList<>();
         mOtherAppIds = new WatchedSparseArray<>();
@@ -587,12 +587,13 @@
         mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
         mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
 
+        mDomainVerificationManager = domainVerificationManager;
+
         mPackages.registerObserver(mObserver);
         mInstallerPackages.registerObserver(mObserver);
         mKernelMapping.registerObserver(mObserver);
         mDisabledSysPackages.registerObserver(mObserver);
         mBlockUninstallPackages.registerObserver(mObserver);
-        mRestoredIntentFilterVerifications.registerObserver(mObserver);
         mVersion.registerObserver(mObserver);
         mPreferredActivities.registerObserver(mObserver);
         mPersistentPreferredActivities.registerObserver(mObserver);
@@ -602,7 +603,6 @@
         mOtherAppIds.registerObserver(mObserver);
         mRenamedPackages.registerObserver(mObserver);
         mDefaultBrowserApp.registerObserver(mObserver);
-        mNextAppLinkGeneration.registerObserver(mObserver);
 
         Watchable.verifyWatchedAttributes(this, mObserver);
     }
@@ -629,11 +629,12 @@
         mBackupStoppedPackagesFilename = null;
         mKernelMappingFilename = null;
 
+        mDomainVerificationManager = r.mDomainVerificationManager;
+
         mInstallerPackages.addAll(r.mInstallerPackages);
         mKernelMapping.putAll(r.mKernelMapping);
         mDisabledSysPackages.putAll(r.mDisabledSysPackages);
         mBlockUninstallPackages.snapshot(r.mBlockUninstallPackages);
-        mRestoredIntentFilterVerifications.putAll(r.mRestoredIntentFilterVerifications);
         mVersion.putAll(r.mVersion);
         mVerifierDeviceIdentity = r.mVerifierDeviceIdentity;
         WatchedSparseArray.snapshot(
@@ -649,7 +650,6 @@
         mKeySetRefs.putAll(r.mKeySetRefs);
         mRenamedPackages.snapshot(r.mRenamedPackages);
         mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp);
-        mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration);
         // mReadMessages
         mPendingPackages.addAll(r.mPendingPackages);
         mSystemDir = null;
@@ -766,7 +766,8 @@
                 p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
                 p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
-                p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups);
+                p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups,
+                mDomainVerificationManager.generateNewId());
         if (ret != null) {
             ret.getPkgState().setUpdatedSystemApp(false);
         }
@@ -786,7 +787,8 @@
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
             pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries,
-            long[] usesStaticLibraryNames, Map<String, ArraySet<String>> mimeGroups) {
+            long[] usesStaticLibraryNames, Map<String, ArraySet<String>> mimeGroups,
+            @NonNull UUID domainSetId) {
         PackageSetting p = mPackages.get(name);
         if (p != null) {
             if (p.appId == uid) {
@@ -799,7 +801,7 @@
         p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString,
                 primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags,
                 pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames,
-                mimeGroups);
+                mimeGroups, domainSetId);
         p.appId = uid;
         if (registerExistingAppIdLPw(uid, p, name)) {
             mPackages.put(name, p);
@@ -863,7 +865,7 @@
             UserHandle installUser, boolean allowInstall, boolean instantApp,
             boolean virtualPreload, UserManagerService userManager,
             String[] usesStaticLibraries, long[] usesStaticLibrariesVersions,
-            Set<String> mimeGroupNames) {
+            Set<String> mimeGroupNames, @NonNull UUID domainSetId) {
         final PackageSetting pkgSetting;
         if (originalPkg != null) {
             if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
@@ -883,12 +885,13 @@
             pkgSetting.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
             // Update new package state.
             pkgSetting.setTimeStamp(codePath.lastModified());
+            pkgSetting.setDomainSetId(domainSetId);
         } else {
             pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
                     legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
                     null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
                     0 /*sharedUserId*/, usesStaticLibraries,
-                    usesStaticLibrariesVersions, createMimeGroups(mimeGroupNames));
+                    usesStaticLibrariesVersions, createMimeGroups(mimeGroupNames), domainSetId);
             pkgSetting.setTimeStamp(codePath.lastModified());
             pkgSetting.sharedUser = sharedUser;
             // If this is not a system app, it starts out stopped.
@@ -925,8 +928,6 @@
                                 null /*lastDisableAppCaller*/,
                                 null /*enabledComponents*/,
                                 null /*disabledComponents*/,
-                                INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
-                                0 /*linkGeneration*/,
                                 PackageManager.INSTALL_REASON_UNKNOWN,
                                 PackageManager.UNINSTALL_REASON_UNKNOWN,
                                 null /*harmfulAppWarning*/);
@@ -983,7 +984,7 @@
             @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags,
             int pkgPrivateFlags, @NonNull UserManagerService userManager,
             @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions,
-            @Nullable Set<String> mimeGroupNames)
+            @Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId)
                     throws PackageManagerException {
         final String pkgName = pkgSetting.name;
         if (pkgSetting.sharedUser != sharedUser) {
@@ -1059,6 +1060,7 @@
             pkgSetting.usesStaticLibrariesVersions = null;
         }
         pkgSetting.updateMimeGroups(mimeGroupNames);
+        pkgSetting.setDomainSetId(domainSetId);
     }
 
     /**
@@ -1168,15 +1170,6 @@
                 replaceAppIdLPw(p.appId, sharedUser);
             }
         }
-
-        IntentFilterVerificationInfo ivi = mRestoredIntentFilterVerifications.get(p.name);
-        if (ivi != null) {
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.i(TAG, "Applying restored IVI for " + p.name + " : " + ivi.getStatusString());
-            }
-            mRestoredIntentFilterVerifications.remove(p.name);
-            p.setIntentFilterVerificationInfo(ivi);
-        }
     }
 
     int removePackageLPw(String name) {
@@ -1307,129 +1300,6 @@
         return cpir;
     }
 
-    /**
-     * The following functions suppose that you have a lock for managing access to the
-     * mIntentFiltersVerifications map.
-     */
-
-    /* package protected */
-    IntentFilterVerificationInfo getIntentFilterVerificationLPr(String packageName) {
-        PackageSetting ps = mPackages.get(packageName);
-        if (ps == null) {
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.w(PackageManagerService.TAG, "No package known: " + packageName);
-            }
-            return null;
-        }
-        return ps.getIntentFilterVerificationInfo();
-    }
-
-    /* package protected */
-    IntentFilterVerificationInfo createIntentFilterVerificationIfNeededLPw(String packageName,
-            ArraySet<String> domains) {
-        PackageSetting ps = mPackages.get(packageName);
-        if (ps == null) {
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.w(PackageManagerService.TAG, "No package known: " + packageName);
-            }
-            return null;
-        }
-        IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
-        if (ivi == null) {
-            ivi = new IntentFilterVerificationInfo(packageName, domains);
-            ps.setIntentFilterVerificationInfo(ivi);
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.d(PackageManagerService.TAG,
-                        "Creating new IntentFilterVerificationInfo for pkg: " + packageName);
-            }
-        } else {
-            ivi.setDomains(domains);
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.d(PackageManagerService.TAG,
-                        "Setting domains to existing IntentFilterVerificationInfo for pkg: " +
-                                packageName + " and with domains: " + ivi.getDomainsString());
-            }
-        }
-        return ivi;
-    }
-
-    int getIntentFilterVerificationStatusLPr(String packageName, int userId) {
-        PackageSetting ps = mPackages.get(packageName);
-        if (ps == null) {
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.w(PackageManagerService.TAG, "No package known: " + packageName);
-            }
-            return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-        }
-        return (int)(ps.getDomainVerificationStatusForUser(userId) >> 32);
-    }
-
-    boolean updateIntentFilterVerificationStatusLPw(String packageName, final int status, int userId) {
-        // Update the status for the current package
-        PackageSetting current = mPackages.get(packageName);
-        if (current == null) {
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.w(PackageManagerService.TAG, "No package known: " + packageName);
-            }
-            return false;
-        }
-
-        final int alwaysGeneration;
-        if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
-            alwaysGeneration = mNextAppLinkGeneration.get(userId) + 1;
-            mNextAppLinkGeneration.put(userId, alwaysGeneration);
-        } else {
-            alwaysGeneration = 0;
-        }
-
-        current.setDomainVerificationStatusForUser(status, alwaysGeneration, userId);
-        return true;
-    }
-
-    /**
-     * Used for Settings App and PackageManagerService dump. Should be read only.
-     */
-    List<IntentFilterVerificationInfo> getIntentFilterVerificationsLPr(
-            String packageName) {
-        if (packageName == null) {
-            return Collections.<IntentFilterVerificationInfo>emptyList();
-        }
-        ArrayList<IntentFilterVerificationInfo> result = new ArrayList<>();
-        for (PackageSetting ps : mPackages.values()) {
-            IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
-            if (ivi == null || TextUtils.isEmpty(ivi.getPackageName()) ||
-                    !ivi.getPackageName().equalsIgnoreCase(packageName)) {
-                continue;
-            }
-            result.add(ivi);
-        }
-        return result;
-    }
-
-    boolean removeIntentFilterVerificationLPw(String packageName, int userId,
-            boolean alsoResetStatus) {
-        PackageSetting ps = mPackages.get(packageName);
-        if (ps == null) {
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.w(PackageManagerService.TAG, "No package known: " + packageName);
-            }
-            return false;
-        }
-        if (alsoResetStatus) {
-            ps.clearDomainVerificationStatusForUser(userId);
-        }
-        ps.setIntentFilterVerificationInfo(null);
-        return true;
-    }
-
-    boolean removeIntentFilterVerificationLPw(String packageName, int[] userIds) {
-        boolean result = false;
-        for (int userId : userIds) {
-            result |= removeIntentFilterVerificationLPw(packageName, userId, true);
-        }
-        return result;
-    }
-
     String removeDefaultBrowserPackageNameLPw(int userId) {
         return (userId == UserHandle.USER_ALL) ? null : mDefaultBrowserApp.removeReturnOld(userId);
     }
@@ -1591,40 +1461,7 @@
         }
     }
 
-    private void readDomainVerificationLPw(TypedXmlPullParser parser,
-            PackageSettingBase packageSetting) throws XmlPullParserException, IOException {
-        IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
-        packageSetting.setIntentFilterVerificationInfo(ivi);
-        if (DEBUG_PARSER) {
-            Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName());
-        }
-    }
-
-    private void readRestoredIntentFilterVerifications(TypedXmlPullParser parser)
-            throws XmlPullParserException, IOException {
-        int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-            final String tagName = parser.getName();
-            if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
-                IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
-                if (DEBUG_DOMAIN_VERIFICATION) {
-                    Slog.i(TAG, "Restored IVI for " + ivi.getPackageName()
-                            + " status=" + ivi.getStatusString());
-                }
-                mRestoredIntentFilterVerifications.put(ivi.getPackageName(), ivi);
-            } else {
-                Slog.w(TAG, "Unknown element: " + tagName);
-                XmlUtils.skipCurrentTag(parser);
-            }
-        }
-    }
-
-    void readDefaultAppsLPw(TypedXmlPullParser parser, int userId)
+    void readDefaultAppsLPw(XmlPullParser parser, int userId)
             throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
         int type;
@@ -1727,8 +1564,6 @@
                                 null /*lastDisableAppCaller*/,
                                 null /*enabledComponents*/,
                                 null /*disabledComponents*/,
-                                INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
-                                0 /*linkGeneration*/,
                                 PackageManager.INSTALL_REASON_UNKNOWN,
                                 PackageManager.UNINSTALL_REASON_UNKNOWN,
                                 null /*harmfulAppWarning*/);
@@ -1753,8 +1588,6 @@
                 return;
             }
 
-            int maxAppLinkGeneration = 0;
-
             int outerDepth = parser.getDepth();
             PackageSetting ps = null;
             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1817,11 +1650,6 @@
                     final int verifState = parser.getAttributeInt(null,
                             ATTR_DOMAIN_VERIFICATON_STATE,
                             PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
-                    final int linkGeneration =
-                            parser.getAttributeInt(null, ATTR_APP_LINK_GENERATION, 0);
-                    if (linkGeneration > maxAppLinkGeneration) {
-                        maxAppLinkGeneration = linkGeneration;
-                    }
                     final int installReason = parser.getAttributeInt(null, ATTR_INSTALL_REASON,
                             PackageManager.INSTALL_REASON_UNKNOWN);
                     final int uninstallReason = parser.getAttributeInt(null, ATTR_UNINSTALL_REASON,
@@ -1897,9 +1725,10 @@
                     }
                     ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
                             hidden, distractionFlags, suspended, suspendParamsMap,
-                            instantApp, virtualPreload,
-                            enabledCaller, enabledComponents, disabledComponents, verifState,
-                            linkGeneration, installReason, uninstallReason, harmfulAppWarning);
+                            instantApp, virtualPreload, enabledCaller, enabledComponents,
+                            disabledComponents, installReason, uninstallReason, harmfulAppWarning);
+
+                    mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
                 } else if (tagName.equals("preferred-activities")) {
                     readPreferredActivitiesLPw(parser, userId);
                 } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -1918,9 +1747,6 @@
             }
 
             str.close();
-
-            mNextAppLinkGeneration.put(userId, maxAppLinkGeneration + 1);
-
         } catch (XmlPullParserException e) {
             mReadMessages.append("Error reading: " + e.toString());
             PackageManagerService.reportSettingsProblem(Log.ERROR,
@@ -2038,77 +1864,7 @@
         serializer.endTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS);
     }
 
-    void writeDomainVerificationsLPr(TypedXmlSerializer serializer,
-                                     IntentFilterVerificationInfo verificationInfo)
-            throws IllegalArgumentException, IllegalStateException, IOException {
-        if (verificationInfo != null && verificationInfo.getPackageName() != null) {
-            serializer.startTag(null, TAG_DOMAIN_VERIFICATION);
-            verificationInfo.writeToXml(serializer);
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.d(TAG, "Wrote domain verification for package: "
-                        + verificationInfo.getPackageName());
-            }
-            serializer.endTag(null, TAG_DOMAIN_VERIFICATION);
-        }
-    }
-
-    // Specifically for backup/restore
-    void writeAllDomainVerificationsLPr(TypedXmlSerializer serializer, int userId)
-            throws IllegalArgumentException, IllegalStateException, IOException {
-        serializer.startTag(null, TAG_ALL_INTENT_FILTER_VERIFICATION);
-        final int N = mPackages.size();
-        for (int i = 0; i < N; i++) {
-            PackageSetting ps = mPackages.valueAt(i);
-            IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
-            if (ivi != null) {
-                writeDomainVerificationsLPr(serializer, ivi);
-            }
-        }
-        serializer.endTag(null, TAG_ALL_INTENT_FILTER_VERIFICATION);
-    }
-
-    // Specifically for backup/restore
-    void readAllDomainVerificationsLPr(TypedXmlPullParser parser, int userId)
-            throws XmlPullParserException, IOException {
-        mRestoredIntentFilterVerifications.clear();
-
-        int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            String tagName = parser.getName();
-            if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
-                IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
-                final String pkgName = ivi.getPackageName();
-                final PackageSetting ps = mPackages.get(pkgName);
-                if (ps != null) {
-                    // known/existing package; update in place
-                    ps.setIntentFilterVerificationInfo(ivi);
-                    if (DEBUG_DOMAIN_VERIFICATION) {
-                        Slog.d(TAG, "Restored IVI for existing app " + pkgName
-                                + " status=" + ivi.getStatusString());
-                    }
-                } else {
-                    mRestoredIntentFilterVerifications.put(pkgName, ivi);
-                    if (DEBUG_DOMAIN_VERIFICATION) {
-                        Slog.d(TAG, "Restored IVI for pending app " + pkgName
-                                + " status=" + ivi.getStatusString());
-                    }
-                }
-            } else {
-                PackageManagerService.reportSettingsProblem(Log.WARN,
-                        "Unknown element under <all-intent-filter-verification>: "
-                        + parser.getName());
-                XmlUtils.skipCurrentTag(parser);
-            }
-        }
-    }
-
-    void writeDefaultAppsLPr(TypedXmlSerializer serializer, int userId)
+    void writeDefaultAppsLPr(XmlSerializer serializer, int userId)
             throws IllegalArgumentException, IllegalStateException, IOException {
         serializer.startTag(null, TAG_DEFAULT_APPS);
         String defaultBrowser = mDefaultBrowserApp.get(userId);
@@ -2217,15 +1973,6 @@
                                 ustate.lastDisableAppCaller);
                     }
                 }
-                if (ustate.domainVerificationStatus !=
-                        PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
-                    serializer.attributeInt(null, ATTR_DOMAIN_VERIFICATON_STATE,
-                            ustate.domainVerificationStatus);
-                }
-                if (ustate.appLinkGeneration != 0) {
-                    serializer.attributeInt(null, ATTR_APP_LINK_GENERATION,
-                            ustate.appLinkGeneration);
-                }
                 if (ustate.installReason != PackageManager.INSTALL_REASON_UNKNOWN) {
                     serializer.attributeInt(null, ATTR_INSTALL_REASON, ustate.installReason);
                 }
@@ -2569,22 +2316,7 @@
                 }
             }
 
-            final int numIVIs = mRestoredIntentFilterVerifications.size();
-            if (numIVIs > 0) {
-                if (DEBUG_DOMAIN_VERIFICATION) {
-                    Slog.i(TAG, "Writing restored-ivi entries to packages.xml");
-                }
-                serializer.startTag(null, "restored-ivi");
-                for (int i = 0; i < numIVIs; i++) {
-                    IntentFilterVerificationInfo ivi = mRestoredIntentFilterVerifications.valueAt(i);
-                    writeDomainVerificationsLPr(serializer, ivi);
-                }
-                serializer.endTag(null, "restored-ivi");
-            } else {
-                if (DEBUG_DOMAIN_VERIFICATION) {
-                    Slog.i(TAG, "  no restored IVI entries to write");
-                }
-            }
+            mDomainVerificationManager.writeSettings(serializer);
 
             mKeySetManagerService.writeKeySetManagerServiceLPr(serializer);
 
@@ -2961,6 +2693,8 @@
         serializer.attributeFloat(null, "loadingProgress",
                 pkg.getIncrementalStates().getProgress());
 
+        serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString());
+
         writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
 
         pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
@@ -2973,7 +2707,7 @@
         writeSigningKeySetLPr(serializer, pkg.keySetData);
         writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
         writeKeySetAliasesLPr(serializer, pkg.keySetData);
-        writeDomainVerificationsLPr(serializer, pkg.verificationInfo);
+        mDomainVerificationManager.writeLegacySettings(serializer, pkg.name);
         writeMimeGroupLPr(serializer, pkg.mimeGroups);
 
         serializer.endTag(null, "package");
@@ -3104,8 +2838,6 @@
                     if (nname != null && oname != null) {
                         mRenamedPackages.put(nname, oname);
                     }
-                } else if (tagName.equals("restored-ivi")) {
-                    readRestoredIntentFilterVerifications(parser);
                 } else if (tagName.equals("last-platform-version")) {
                     // Upgrade from older XML schema
                     final VersionInfo internal = findOrCreateVersion(
@@ -3147,6 +2879,11 @@
                     ver.sdkVersion = parser.getAttributeInt(null, ATTR_SDK_VERSION);
                     ver.databaseVersion = parser.getAttributeInt(null, ATTR_DATABASE_VERSION);
                     ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT);
+                } else if (tagName.equals(DomainVerificationPersistence.TAG_DOMAIN_VERIFICATIONS)) {
+                    mDomainVerificationManager.readSettings(parser);
+                } else if (tagName.equals(
+                        DomainVerificationLegacySettings.TAG_DOMAIN_VERIFICATIONS_LEGACY)) {
+                    mDomainVerificationManager.readLegacySettings(parser);
                 } else {
                     Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
                             + parser.getName());
@@ -3628,9 +3365,15 @@
         if (codePathStr.contains("/priv-app/")) {
             pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
         }
+
+        // When reading a disabled setting, use a disabled domainSetId, which makes it easier to
+        // debug invalid entries. The actual logic for migrating to a new ID is done in other
+        // methods that use DomainVerificationManagerInternal#generateNewId
+        UUID domainSetId = DomainVerificationManagerInternal.DISABLED_ID;
         PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
                 legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr,
-                versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null);
+                versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null,
+                domainSetId);
         long timeStamp = parser.getAttributeLongHex(null, "ft", 0);
         if (timeStamp == 0) {
             timeStamp = parser.getAttributeLong(null, "ts", 0);
@@ -3703,6 +3446,7 @@
         boolean isStartable = false;
         boolean isLoading = false;
         float loadingProgress = 0;
+        UUID domainSetId;
         try {
             name = parser.getAttributeValue(null, ATTR_NAME);
             realName = parser.getAttributeValue(null, "realName");
@@ -3739,6 +3483,15 @@
             categoryHint = parser.getAttributeInt(null, "categoryHint",
                     ApplicationInfo.CATEGORY_UNDEFINED);
 
+            String domainSetIdString = parser.getAttributeValue(null, "domainSetId");
+
+            if (TextUtils.isEmpty(domainSetIdString)) {
+                // If empty, assume restoring from previous platform version and generate an ID
+                domainSetId = mDomainVerificationManager.generateNewId();
+            } else {
+                domainSetId = UUID.fromString(domainSetIdString);
+            }
+
             systemStr = parser.getAttributeValue(null, "publicFlags");
             if (systemStr != null) {
                 try {
@@ -3810,7 +3563,7 @@
                         legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString,
                         cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags,
                         null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/,
-                        null /*mimeGroups*/);
+                        null /*mimeGroups*/, domainSetId);
                 if (PackageManagerService.DEBUG_SETTINGS)
                     Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
                             + userId + " pkg=" + packageSetting);
@@ -3831,7 +3584,7 @@
                             versionCode, pkgFlags, pkgPrivateFlags, sharedUserId,
                             null /*usesStaticLibraries*/,
                             null /*usesStaticLibraryVersions*/,
-                            null /*mimeGroups*/);
+                            null /*mimeGroups*/, domainSetId);
                     packageSetting.setTimeStamp(timeStamp);
                     packageSetting.firstInstallTime = firstInstallTime;
                     packageSetting.lastUpdateTime = lastUpdateTime;
@@ -3946,7 +3699,11 @@
                     packageSetting.installSource =
                             packageSetting.installSource.setInitiatingPackageSignatures(signatures);
                 } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
-                    readDomainVerificationLPw(parser, packageSetting);
+                    IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
+                    mDomainVerificationManager.addLegacySetting(packageSetting.name, ivi);
+                    if (DEBUG_PARSER) {
+                        Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName());
+                    }
                 } else if (tagName.equals(TAG_MIME_GROUP)) {
                     packageSetting.mimeGroups = readMimeGroupLPw(parser, packageSetting.mimeGroups);
                 } else if (tagName.equals(TAG_USES_STATIC_LIB)) {
@@ -4212,6 +3969,7 @@
         removeCrossProfileIntentFiltersLPw(userId);
 
         mRuntimePermissionsPersistence.onUserRemovedLPw(userId);
+        mDomainVerificationManager.clearUser(userId);
 
         writePackageListLPr();
 
diff --git a/services/core/java/com/android/server/pm/SettingsXml.java b/services/core/java/com/android/server/pm/SettingsXml.java
new file mode 100644
index 0000000..9588a27
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SettingsXml.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Stack;
+
+/**
+ * A very specialized serialization/parsing wrapper around {@link TypedXmlSerializer} and {@link
+ * TypedXmlPullParser} intended for use with PackageManager related settings files.
+ * Assumptions/chosen behaviors:
+ * <ul>
+ *     <li>No namespace support</li>
+ *     <li>Data for a parent object is stored as attributes</li>
+ *     <li>All attribute read methods return a default false, -1, or null</li>
+ *     <li>Default values will not be written</li>
+ *     <li>Children are sub-elements</li>
+ *     <li>Collections are repeated sub-elements, no attribute support for collections</li>
+ * </ul>
+ */
+public class SettingsXml {
+
+    private static final String TAG = "SettingsXml";
+
+    private static final boolean DEBUG_THROW_EXCEPTIONS = false;
+
+    private static final String FEATURE_INDENT =
+            "http://xmlpull.org/v1/doc/features.html#indent-output";
+
+    private static final int DEFAULT_NUMBER = -1;
+
+    public static Serializer serializer(TypedXmlSerializer serializer) {
+        return new Serializer(serializer);
+    }
+
+    public static ReadSection parser(TypedXmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        return new ReadSectionImpl(parser);
+    }
+
+    public static class Serializer implements AutoCloseable {
+
+        @NonNull
+        private final TypedXmlSerializer mXmlSerializer;
+
+        private final WriteSectionImpl mWriteSection;
+
+        private Serializer(TypedXmlSerializer serializer) {
+            mXmlSerializer = serializer;
+            mWriteSection = new WriteSectionImpl(mXmlSerializer);
+        }
+
+        public WriteSection startSection(@NonNull String sectionName) throws IOException {
+            return mWriteSection.startSection(sectionName);
+        }
+
+        @Override
+        public void close() throws IOException {
+            mWriteSection.closeCompletely();
+            mXmlSerializer.endDocument();
+        }
+    }
+
+    public interface ReadSection extends AutoCloseable {
+
+        @NonNull
+        String getName();
+
+        @NonNull
+        String getDescription();
+
+        boolean has(String attrName);
+
+        @Nullable
+        String getString(String attrName);
+
+        /**
+         * @return value as String or {@param defaultValue} if doesn't exist
+         */
+        @NonNull
+        String getString(String attrName, @NonNull String defaultValue);
+
+        /**
+         * @return value as boolean or false if doesn't exist
+         */
+        boolean getBoolean(String attrName);
+
+        /**
+         * @return value as boolean or {@param defaultValue} if doesn't exist
+         */
+        boolean getBoolean(String attrName, boolean defaultValue);
+
+        /**
+         * @return value as int or {@link #DEFAULT_NUMBER} if doesn't exist
+         */
+        int getInt(String attrName);
+
+        /**
+         * @return value as int or {@param defaultValue} if doesn't exist
+         */
+        int getInt(String attrName, int defaultValue);
+
+        /**
+         * @return value as long or {@link #DEFAULT_NUMBER} if doesn't exist
+         */
+        long getLong(String attrName);
+
+        /**
+         * @return value as long or {@param defaultValue} if doesn't exist
+         */
+        long getLong(String attrName, int defaultValue);
+
+        ChildSection children();
+    }
+
+    /**
+     * <pre><code>
+     * ChildSection child = parentSection.children();
+     * while (child.moveToNext(TAG_CHILD)) {
+     *     String readValue = child.getString(...);
+     *     ...
+     * }
+     * </code></pre>
+     */
+    public interface ChildSection extends ReadSection {
+        boolean moveToNext();
+
+        boolean moveToNext(@NonNull String expectedChildTagName);
+    }
+
+    public static class ReadSectionImpl implements ChildSection {
+
+        @Nullable
+        private final InputStream mInput;
+
+        @NonNull
+        private final TypedXmlPullParser mParser;
+
+        @NonNull
+        private final Stack<Integer> mDepthStack = new Stack<>();
+
+        public ReadSectionImpl(@NonNull InputStream input)
+                throws IOException, XmlPullParserException {
+            mInput = input;
+            mParser = Xml.newFastPullParser();
+            mParser.setInput(mInput, StandardCharsets.UTF_8.name());
+            moveToFirstTag();
+        }
+
+        public ReadSectionImpl(@NonNull TypedXmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            mInput = null;
+            mParser = parser;
+            moveToFirstTag();
+        }
+
+        private void moveToFirstTag() throws IOException, XmlPullParserException {
+            // Move to first tag
+            int type;
+            //noinspection StatementWithEmptyBody
+            while ((type = mParser.next()) != XmlPullParser.START_TAG
+                    && type != XmlPullParser.END_DOCUMENT) {
+            }
+        }
+
+        @NonNull
+        @Override
+        public String getName() {
+            return mParser.getName();
+        }
+
+        @NonNull
+        @Override
+        public String getDescription() {
+            return mParser.getPositionDescription();
+        }
+
+        @Override
+        public boolean has(String attrName) {
+            return mParser.getAttributeValue(null, attrName) != null;
+        }
+
+        @Nullable
+        @Override
+        public String getString(String attrName) {
+            return mParser.getAttributeValue(null, attrName);
+        }
+
+        @NonNull
+        @Override
+        public String getString(String attrName, @NonNull String defaultValue) {
+            String value = mParser.getAttributeValue(null, attrName);
+            if (value == null) {
+                return defaultValue;
+            }
+            return value;
+        }
+
+        @Override
+        public boolean getBoolean(String attrName) {
+            return getBoolean(attrName, false);
+        }
+
+        @Override
+        public boolean getBoolean(String attrName, boolean defaultValue) {
+            return mParser.getAttributeBoolean(null, attrName, defaultValue);
+        }
+
+        @Override
+        public int getInt(String attrName) {
+            return getInt(attrName, DEFAULT_NUMBER);
+        }
+
+        @Override
+        public int getInt(String attrName, int defaultValue) {
+            return mParser.getAttributeInt(null, attrName, defaultValue);
+        }
+
+        @Override
+        public long getLong(String attrName) {
+            return getLong(attrName, DEFAULT_NUMBER);
+        }
+
+        @Override
+        public long getLong(String attrName, int defaultValue) {
+            return mParser.getAttributeLong(null, attrName, defaultValue);
+        }
+
+        @Override
+        public ChildSection children() {
+            mDepthStack.push(mParser.getDepth());
+            return this;
+        }
+
+        @Override
+        public boolean moveToNext() {
+            return moveToNextInternal(null);
+        }
+
+        @Override
+        public boolean moveToNext(@NonNull String expectedChildTagName) {
+            return moveToNextInternal(expectedChildTagName);
+        }
+
+        private boolean moveToNextInternal(@Nullable String expectedChildTagName) {
+            try {
+                int depth = mDepthStack.peek();
+                boolean hasTag = false;
+                int type;
+                while (!hasTag
+                        && (type = mParser.next()) != XmlPullParser.END_DOCUMENT
+                        && (type != XmlPullParser.END_TAG || mParser.getDepth() > depth)) {
+                    if (type != XmlPullParser.START_TAG) {
+                        continue;
+                    }
+
+                    if (expectedChildTagName != null
+                            && !expectedChildTagName.equals(mParser.getName())) {
+                        continue;
+                    }
+
+                    hasTag = true;
+                }
+
+                if (!hasTag) {
+                    mDepthStack.pop();
+                }
+
+                return hasTag;
+            } catch (Exception ignored) {
+                return false;
+            }
+        }
+
+        @Override
+        public void close() throws Exception {
+            if (mDepthStack.isEmpty()) {
+                Slog.wtf(TAG, "Children depth stack was not empty, data may have been lost",
+                        new Exception());
+            }
+            if (mInput != null) {
+                mInput.close();
+            }
+        }
+    }
+
+    public interface WriteSection extends AutoCloseable {
+
+        WriteSection startSection(@NonNull String sectionName) throws IOException;
+
+        WriteSection attribute(String attrName, @Nullable String value) throws IOException;
+
+        WriteSection attribute(String attrName, int value) throws IOException;
+
+        WriteSection attribute(String attrName, long value) throws IOException;
+
+        WriteSection attribute(String attrName, boolean value) throws IOException;
+
+        @Override
+        void close() throws IOException;
+
+        void finish() throws IOException;
+    }
+
+    private static class WriteSectionImpl implements WriteSection {
+
+        @NonNull
+        private final TypedXmlSerializer mXmlSerializer;
+
+        @NonNull
+        private final Stack<String> mTagStack = new Stack<>();
+
+        private WriteSectionImpl(@NonNull TypedXmlSerializer xmlSerializer) {
+            mXmlSerializer = xmlSerializer;
+        }
+
+        @Override
+        public WriteSection startSection(@NonNull String sectionName) throws IOException {
+            // Try to start the tag first before we push it to the stack
+            mXmlSerializer.startTag(null, sectionName);
+            mTagStack.push(sectionName);
+            return this;
+        }
+
+        @Override
+        public WriteSection attribute(String attrName, String value) throws IOException {
+            if (value != null) {
+                mXmlSerializer.attribute(null, attrName, value);
+            }
+            return this;
+        }
+
+        @Override
+        public WriteSection attribute(String attrName, int value) throws IOException {
+            if (value != DEFAULT_NUMBER) {
+                mXmlSerializer.attributeInt(null, attrName, value);
+            }
+            return this;
+        }
+
+        @Override
+        public WriteSection attribute(String attrName, long value) throws IOException {
+            if (value != DEFAULT_NUMBER) {
+                mXmlSerializer.attributeLong(null, attrName, value);
+            }
+            return this;
+        }
+
+        @Override
+        public WriteSection attribute(String attrName, boolean value) throws IOException {
+            if (value) {
+                mXmlSerializer.attributeBoolean(null, attrName, value);
+            }
+            return this;
+        }
+
+        @Override
+        public void finish() throws IOException {
+            close();
+        }
+
+        @Override
+        public void close() throws IOException {
+            mXmlSerializer.endTag(null, mTagStack.pop());
+        }
+
+        private void closeCompletely() throws IOException {
+            if (DEBUG_THROW_EXCEPTIONS && mTagStack != null && !mTagStack.isEmpty()) {
+                throw new IllegalStateException(
+                        "tag stack is not empty when closing, contains " + mTagStack);
+            } else if (mTagStack != null) {
+                while (!mTagStack.isEmpty()) {
+                    close();
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index c182d68..c17d2e4 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -298,4 +298,11 @@
      * Gets all {@link UserInfo UserInfos}.
      */
     public abstract @NonNull UserInfo[] getUserInfos();
+
+    /**
+     * Sets all default cross profile intent filters between {@code parentUserId} and
+     * {@code profileUserId}.
+     */
+    public abstract void setDefaultCrossProfileIntentFilters(
+            @UserIdInt int parentUserId, @UserIdInt int profileUserId);
 }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f43240b..753f22d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -80,6 +80,7 @@
 import android.os.UserManager.EnforcingUser;
 import android.os.UserManager.QuietModeFlag;
 import android.os.storage.StorageManager;
+import android.provider.Settings;
 import android.security.GateKeeper;
 import android.service.gatekeeper.IGateKeeperService;
 import android.stats.devicepolicy.DevicePolicyEnums;
@@ -108,6 +109,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.BundleUtils;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
 import com.android.server.SystemService;
@@ -1960,11 +1962,11 @@
         final Bundle global = mDevicePolicyGlobalUserRestrictions.mergeAll();
         final RestrictionsSet local = getDevicePolicyLocalRestrictionsForTargetUserLR(userId);
 
-        if (UserRestrictionsUtils.isEmpty(global) && local.isEmpty()) {
+        if (BundleUtils.isEmpty(global) && local.isEmpty()) {
             // Common case first.
             return baseRestrictions;
         }
-        final Bundle effective = UserRestrictionsUtils.clone(baseRestrictions);
+        final Bundle effective = BundleUtils.clone(baseRestrictions);
         UserRestrictionsUtils.merge(effective, global);
         UserRestrictionsUtils.merge(effective, local.mergeAll());
 
@@ -2105,7 +2107,7 @@
     @Override
     public Bundle getUserRestrictions(@UserIdInt int userId) {
         checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
-        return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
+        return BundleUtils.clone(getEffectiveUserRestrictions(userId));
     }
 
     @Override
@@ -2129,7 +2131,7 @@
         synchronized (mRestrictionsLock) {
             // Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create
             // a copy.
-            final Bundle newRestrictions = UserRestrictionsUtils.clone(
+            final Bundle newRestrictions = BundleUtils.clone(
                     mBaseUserRestrictions.getRestrictions(userId));
             newRestrictions.putBoolean(key, value);
 
@@ -2727,7 +2729,7 @@
         if (userVersion < 7) {
             // Previously only one user could enforce global restrictions, now it is per-user.
             synchronized (mRestrictionsLock) {
-                if (!UserRestrictionsUtils.isEmpty(oldGlobalUserRestrictions)
+                if (!BundleUtils.isEmpty(oldGlobalUserRestrictions)
                         && mDeviceOwnerUserId != UserHandle.USER_NULL) {
                     mDevicePolicyGlobalUserRestrictions.updateRestrictions(
                             mDeviceOwnerUserId, oldGlobalUserRestrictions);
@@ -3562,6 +3564,9 @@
             t.traceBegin("PM.onNewUserCreated-" + userId);
             mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
             t.traceEnd();
+            applyDefaultUserSettings(userTypeDetails, userId);
+            setDefaultCrossProfileIntentFilters(userId, userTypeDetails, restrictions, parentId);
+
             if (preCreate) {
                 // Must start user (which will be stopped right away, through
                 // UserController.finishUserUnlockedCompleted) so services can properly
@@ -3597,6 +3602,78 @@
         return userInfo;
     }
 
+    private void applyDefaultUserSettings(UserTypeDetails userTypeDetails, @UserIdInt int userId) {
+        final Bundle systemSettings = userTypeDetails.getDefaultSystemSettings();
+        final Bundle secureSettings = userTypeDetails.getDefaultSecureSettings();
+        if (systemSettings.isEmpty() && secureSettings.isEmpty()) {
+            return;
+        }
+
+        final int systemSettingsSize = systemSettings.size();
+        final String[] systemSettingsArray = systemSettings.keySet().toArray(
+                new String[systemSettingsSize]);
+        for (int i = 0; i < systemSettingsSize; i++) {
+            final String setting = systemSettingsArray[i];
+            if (!Settings.System.putStringForUser(
+                    mContext.getContentResolver(), setting, systemSettings.getString(setting),
+                    userId)) {
+                Slog.e(LOG_TAG, "Failed to insert default system setting: " + setting);
+            }
+        }
+
+        final int secureSettingsSize = secureSettings.size();
+        final String[] secureSettingsArray = secureSettings.keySet().toArray(
+                new String[secureSettingsSize]);
+        for (int i = 0; i < secureSettingsSize; i++) {
+            final String setting = secureSettingsArray[i];
+            if (!Settings.Secure.putStringForUser(
+                    mContext.getContentResolver(), setting, secureSettings.getString(setting),
+                    userId)) {
+                Slog.e(LOG_TAG, "Failed to insert default secure setting: " + setting);
+            }
+        }
+    }
+
+    /**
+     * Sets all default cross profile intent filters between {@code parentUserId} and
+     * {@code profileUserId}, does nothing if {@code userType} is not a profile.
+     */
+    private void setDefaultCrossProfileIntentFilters(
+            @UserIdInt int profileUserId, UserTypeDetails profileDetails,
+            Bundle profileRestrictions, @UserIdInt int parentUserId) {
+        if (profileDetails == null || !profileDetails.isProfile()) {
+            return;
+        }
+        final List<DefaultCrossProfileIntentFilter> filters =
+                profileDetails.getDefaultCrossProfileIntentFilters();
+        if (filters.isEmpty()) {
+            return;
+        }
+
+        // Skip filters that allow data to be shared into the profile, if admin has disabled it.
+        final boolean disallowSharingIntoProfile =
+                profileRestrictions.getBoolean(
+                        UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
+                        /* defaultValue = */ false);
+        final int size = profileDetails.getDefaultCrossProfileIntentFilters().size();
+        for (int i = 0; i < size; i++) {
+            final DefaultCrossProfileIntentFilter filter =
+                    profileDetails.getDefaultCrossProfileIntentFilters().get(i);
+            if (disallowSharingIntoProfile && filter.letsPersonalDataIntoProfile) {
+                continue;
+            }
+            if (filter.direction == DefaultCrossProfileIntentFilter.Direction.TO_PARENT) {
+                mPm.addCrossProfileIntentFilter(
+                        filter.filter, mContext.getOpPackageName(), profileUserId, parentUserId,
+                        filter.flags);
+            } else {
+                mPm.addCrossProfileIntentFilter(
+                        filter.filter, mContext.getOpPackageName(), parentUserId, profileUserId,
+                        filter.flags);
+            }
+        }
+    }
+
     /**
      * Finds and converts a previously pre-created user into a regular user, if possible.
      *
@@ -3880,6 +3957,7 @@
      */
     @Override
     public boolean removeUser(@UserIdInt int userId) {
+        Slog.i(LOG_TAG, "removeUser u" + userId);
         checkManageOrCreateUsersPermission("Only the system can remove users");
 
         final String restriction = getUserRemovalRestriction(userId);
@@ -5461,6 +5539,15 @@
                 return allInfos;
             }
         }
+
+        @Override
+        public void setDefaultCrossProfileIntentFilters(
+                @UserIdInt int parentUserId, @UserIdInt int profileUserId) {
+            final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(profileUserId);
+            final Bundle restrictions = getEffectiveUserRestrictions(profileUserId);
+            UserManagerService.this.setDefaultCrossProfileIntentFilters(
+                    profileUserId, userTypeDetails, restrictions, parentUserId);
+        }
     }
 
     /**
@@ -5725,8 +5812,8 @@
 
         // merge existing base restrictions with the new type's default restrictions
         synchronized (mRestrictionsLock) {
-            if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) {
-                final Bundle newRestrictions = UserRestrictionsUtils.clone(
+            if (!BundleUtils.isEmpty(newUserType.getDefaultRestrictions())) {
+                final Bundle newRestrictions = BundleUtils.clone(
                         mBaseUserRestrictions.getRestrictions(userInfo.id));
                 UserRestrictionsUtils.merge(newRestrictions,
                         newUserType.getDefaultRestrictions());
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 51dff40..ff90043 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -44,6 +44,7 @@
 
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
+import com.android.server.BundleUtils;
 import com.android.server.LocalServices;
 
 import com.google.android.collect.Sets;
@@ -384,10 +385,6 @@
         return in != null ? in : new Bundle();
     }
 
-    public static boolean isEmpty(@Nullable Bundle in) {
-        return (in == null) || (in.size() == 0);
-    }
-
     /**
      * Returns {@code true} if given bundle is not null and contains {@code true} for a given
      * restriction.
@@ -396,17 +393,6 @@
         return in != null && in.getBoolean(restriction);
     }
 
-    /**
-     * Creates a copy of the {@code in} Bundle.  If {@code in} is null, it'll return an empty
-     * bundle.
-     *
-     * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
-     * {@link Bundle#EMPTY})
-     */
-    public static @NonNull Bundle clone(@Nullable Bundle in) {
-        return (in != null) ? new Bundle(in) : new Bundle();
-    }
-
     public static void merge(@NonNull Bundle dest, @Nullable Bundle in) {
         Objects.requireNonNull(dest);
         Preconditions.checkArgument(dest != in);
@@ -483,10 +469,10 @@
         if (a == b) {
             return true;
         }
-        if (isEmpty(a)) {
-            return isEmpty(b);
+        if (BundleUtils.isEmpty(a)) {
+            return BundleUtils.isEmpty(b);
         }
-        if (isEmpty(b)) {
+        if (BundleUtils.isEmpty(b)) {
             return false;
         }
         for (String key : a.keySet()) {
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 5fa46b9..17ce386 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -28,8 +28,12 @@
 import android.os.UserManager;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.BundleUtils;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Contains the details about a multiuser "user type", such as a
@@ -82,6 +86,24 @@
      */
     private final @Nullable Bundle mDefaultRestrictions;
 
+    /**
+     * List of {@link android.provider.Settings.System} to apply by default to newly created users
+     * of this type.
+     */
+    private final @Nullable Bundle mDefaultSystemSettings;
+
+    /**
+     * List of {@link android.provider.Settings.Secure} to apply by default to newly created users
+     * of this type.
+     */
+    private final @Nullable Bundle mDefaultSecureSettings;
+
+    /**
+     * List of {@link DefaultCrossProfileIntentFilter} to allow by default for newly created
+     * profiles.
+     */
+    private final @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters;
+
 
     // Fields for profiles only, controlling the nature of their badges.
     // All badge information should be set if {@link #hasBadge()} is true.
@@ -133,7 +155,10 @@
             int iconBadge, int badgePlain, int badgeNoBackground,
             @Nullable int[] badgeLabels, @Nullable int[] badgeColors,
             @Nullable int[] darkThemeBadgeColors,
-            @Nullable Bundle defaultRestrictions) {
+            @Nullable Bundle defaultRestrictions,
+            @Nullable Bundle defaultSystemSettings,
+            @Nullable Bundle defaultSecureSettings,
+            @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters) {
         this.mName = name;
         this.mEnabled = enabled;
         this.mMaxAllowed = maxAllowed;
@@ -141,6 +166,9 @@
         this.mBaseType = baseType;
         this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags;
         this.mDefaultRestrictions = defaultRestrictions;
+        this.mDefaultSystemSettings = defaultSystemSettings;
+        this.mDefaultSecureSettings = defaultSecureSettings;
+        this.mDefaultCrossProfileIntentFilters = defaultCrossProfileIntentFilters;
 
         this.mIconBadge = iconBadge;
         this.mBadgePlain = badgePlain;
@@ -263,9 +291,9 @@
         return (mBaseType & UserInfo.FLAG_SYSTEM) != 0;
     }
 
-    /** Returns a Bundle representing the default user restrictions. */
+    /** Returns a {@link Bundle} representing the default user restrictions. */
     @NonNull Bundle getDefaultRestrictions() {
-        return UserRestrictionsUtils.clone(mDefaultRestrictions);
+        return BundleUtils.clone(mDefaultRestrictions);
     }
 
     /** Adds the default user restrictions to the given bundle of restrictions. */
@@ -273,6 +301,24 @@
         UserRestrictionsUtils.merge(currentRestrictions, mDefaultRestrictions);
     }
 
+    /** Returns a {@link Bundle} representing the default system settings. */
+    @NonNull Bundle getDefaultSystemSettings() {
+        return BundleUtils.clone(mDefaultSystemSettings);
+    }
+
+    /** Returns a {@link Bundle} representing the default secure settings. */
+    @NonNull Bundle getDefaultSecureSettings() {
+        return BundleUtils.clone(mDefaultSecureSettings);
+    }
+
+    /** Returns a list of default cross profile intent filters. */
+    @NonNull List<DefaultCrossProfileIntentFilter> getDefaultCrossProfileIntentFilters() {
+        return mDefaultCrossProfileIntentFilters != null
+                ? new ArrayList<>(mDefaultCrossProfileIntentFilters)
+                : Collections.emptyList();
+    }
+
+
     /** Dumps details of the UserTypeDetails. Do not parse this. */
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mName: "); pw.println(mName);
@@ -325,6 +371,10 @@
         private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS;
         private int mDefaultUserInfoPropertyFlags = 0;
         private @Nullable Bundle mDefaultRestrictions = null;
+        private @Nullable Bundle mDefaultSystemSettings = null;
+        private @Nullable Bundle mDefaultSecureSettings = null;
+        private @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters =
+                null;
         private boolean mEnabled = true;
         private int mLabel = Resources.ID_NULL;
         private @Nullable int[] mBadgeLabels = null;
@@ -407,6 +457,22 @@
             return this;
         }
 
+        public Builder setDefaultSystemSettings(@Nullable Bundle settings) {
+            mDefaultSystemSettings = settings;
+            return this;
+        }
+
+        public Builder setDefaultSecureSettings(@Nullable Bundle settings) {
+            mDefaultSecureSettings = settings;
+            return this;
+        }
+
+        public Builder setDefaultCrossProfileIntentFilters(
+                @Nullable List<DefaultCrossProfileIntentFilter> intentFilters) {
+            mDefaultCrossProfileIntentFilters = intentFilters;
+            return this;
+        }
+
         @UserInfoFlag int getBaseType() {
             return mBaseType;
         }
@@ -425,17 +491,28 @@
                 Preconditions.checkArgument(mBadgeColors != null && mBadgeColors.length != 0,
                         "UserTypeDetails " + mName + " has badge but no badgeColors.");
             }
+            if (!isProfile()) {
+                Preconditions.checkArgument(mDefaultCrossProfileIntentFilters == null
+                                || mDefaultCrossProfileIntentFilters.isEmpty(),
+                        "UserTypeDetails %s has a non empty "
+                                + "defaultCrossProfileIntentFilters", mName);
+            }
             return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
                     mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
                     mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
                     mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
-                    mDefaultRestrictions);
+                    mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
+                    mDefaultCrossProfileIntentFilters);
         }
 
         private boolean hasBadge() {
             return mIconBadge != Resources.ID_NULL;
         }
 
+        private boolean isProfile() {
+            return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
+        }
+
         // TODO(b/143784345): Refactor this when we clean up UserInfo.
         private boolean hasValidBaseType() {
             return mBaseType == UserInfo.FLAG_FULL
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 1ffbf60..6aac0b2 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -133,7 +133,9 @@
                         com.android.internal.R.color.profile_badge_1_dark,
                         com.android.internal.R.color.profile_badge_2_dark,
                         com.android.internal.R.color.profile_badge_3_dark)
-                .setDefaultRestrictions(null);
+                .setDefaultRestrictions(getDefaultManagedProfileRestrictions())
+                .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
+                .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter());
     }
 
     /**
@@ -256,12 +258,35 @@
         return restrictions;
     }
 
+    private static Bundle getDefaultManagedProfileRestrictions() {
+        final Bundle restrictions = new Bundle();
+        restrictions.putBoolean(UserManager.DISALLOW_WALLPAPER, true);
+        return restrictions;
+    }
+
+    private static Bundle getDefaultManagedProfileSecureSettings() {
+        // Only add String values to the bundle, settings are written as Strings eventually
+        final Bundle settings = new Bundle();
+        settings.putString(
+                android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, "1");
+        settings.putString(
+                android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, "1");
+        return settings;
+    }
+
+    private static List<DefaultCrossProfileIntentFilter>
+            getDefaultManagedCrossProfileIntentFilter() {
+        return DefaultCrossProfileIntentFiltersUtils.getDefaultManagedProfileFilters();
+    }
+
     /**
      * Reads the given xml parser to obtain device user-type customization, and updates the given
      * map of {@link UserTypeDetails.Builder}s accordingly.
      * <p>
      * The xml file can specify the attributes according to the set... methods below.
      */
+    // TODO(b/176973369): Add parsing logic to support custom settings/filters
+    //  in config_user_types.xml
     @VisibleForTesting
     static void customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders,
             XmlResourceParser parser) {
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 9dde7df..629f120 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -406,7 +406,8 @@
             ParsedProcess proc = procs.get(key);
             retProcs.put(proc.getName(),
                     new ProcessInfo(proc.getName(), new ArraySet<>(proc.getDeniedPermissions()),
-                            proc.getGwpAsanMode()));
+                            proc.getGwpAsanMode(), proc.getMemtagMode(),
+                            proc.getNativeHeapZeroInit()));
         }
         return retProcs;
     }
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
new file mode 100644
index 0000000..36efb39
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.os.Binder;
+import android.os.Build;
+import android.util.ArraySet;
+import android.util.Patterns;
+
+import com.android.server.SystemConfig;
+import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.util.List;
+import java.util.Set;
+
+public class DomainVerificationCollector {
+
+    @NonNull
+    private final PlatformCompat mPlatformCompat;
+
+    @NonNull
+    private final SystemConfig mSystemConfig;
+
+    public DomainVerificationCollector(@NonNull PlatformCompat platformCompat,
+            @NonNull SystemConfig systemConfig) {
+        mPlatformCompat = platformCompat;
+        mSystemConfig = systemConfig;
+    }
+
+    /**
+     * With the updated form of the app links verification APIs, an app will be required to declare
+     * domains inside an intent filter which includes all of the following:
+     * <ul>
+     *     <li>- android:autoVerify="true"</li>
+     *     <li>- Intent.ACTION_VIEW</li>
+     *     <li>- Intent.CATEGORY_BROWSABLE</li>
+     *     <li>- Intent.CATEGORY_DEFAULT</li>
+     *     <li>- Only IntentFilter.SCHEME_HTTP and/or IntentFilter.SCHEME_HTTPS,
+     *           with no other schemes</li>
+     * </ul>
+     *
+     * On prior versions of Android, Intent.CATEGORY_BROWSABLE was not a requirement, other
+     * schemes were allowed, and setting autoVerify to true in any intent filter would implicitly
+     * pretend that all intent filters were set to autoVerify="true".
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    public static final long RESTRICT_DOMAINS = 175408749L;
+
+    @NonNull
+    public ArraySet<String> collectAllWebDomains(@NonNull AndroidPackage pkg) {
+        return collectDomains(pkg, false);
+    }
+
+    /**
+     * Effectively {@link #collectAllWebDomains(AndroidPackage)}, but requires
+     * {@link IntentFilter#getAutoVerify()} == true.
+     */
+    @NonNull
+    public ArraySet<String> collectAutoVerifyDomains(@NonNull AndroidPackage pkg) {
+        return collectDomains(pkg, true);
+    }
+
+    @NonNull
+    private ArraySet<String> collectDomains(@NonNull AndroidPackage pkg,
+            boolean checkAutoVerify) {
+        boolean restrictDomains =
+                DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, RESTRICT_DOMAINS);
+
+        ArraySet<String> domains = new ArraySet<>();
+
+        if (restrictDomains) {
+            collectDomains(domains, pkg, checkAutoVerify);
+        } else {
+            collectDomainsLegacy(domains, pkg, checkAutoVerify);
+        }
+
+        return domains;
+    }
+
+    /** @see #RESTRICT_DOMAINS */
+    private void collectDomainsLegacy(@NonNull Set<String> domains,
+            @NonNull AndroidPackage pkg, boolean checkAutoVerify) {
+        if (!checkAutoVerify) {
+            // Per-domain user selection state doesn't have a V1 equivalent on S, so just use V2
+            collectDomains(domains, pkg, false);
+            return;
+        }
+
+        List<ParsedActivity> activities = pkg.getActivities();
+        int activitiesSize = activities.size();
+
+        // Due to a bug in the platform, for backwards compatibility, assume that all linked apps
+        // require auto verification, even if they forget to mark their manifest as such.
+        boolean needsAutoVerify = mSystemConfig.getLinkedApps().contains(pkg.getPackageName());
+        if (!needsAutoVerify) {
+            for (int activityIndex = 0; activityIndex < activitiesSize && !needsAutoVerify;
+                    activityIndex++) {
+                ParsedActivity activity = activities.get(activityIndex);
+                List<ParsedIntentInfo> intents = activity.getIntents();
+                int intentsSize = intents.size();
+                for (int intentIndex = 0; intentIndex < intentsSize && !needsAutoVerify;
+                        intentIndex++) {
+                    ParsedIntentInfo intent = intents.get(intentIndex);
+                    needsAutoVerify = intent.needsVerification();
+                }
+            }
+
+            if (!needsAutoVerify) {
+                return;
+            }
+        }
+
+        for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
+            ParsedActivity activity = activities.get(activityIndex);
+            List<ParsedIntentInfo> intents = activity.getIntents();
+            int intentsSize = intents.size();
+            for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) {
+                ParsedIntentInfo intent = intents.get(intentIndex);
+                if (intent.handlesWebUris(false)) {
+                    int authorityCount = intent.countDataAuthorities();
+                    for (int index = 0; index < authorityCount; index++) {
+                        domains.add(intent.getDataAuthority(index).getHost());
+                    }
+                }
+            }
+        }
+    }
+
+    /** @see #RESTRICT_DOMAINS */
+    private void collectDomains(@NonNull Set<String> domains,
+            @NonNull AndroidPackage pkg, boolean checkAutoVerify) {
+        List<ParsedActivity> activities = pkg.getActivities();
+        int activitiesSize = activities.size();
+        for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
+            ParsedActivity activity = activities.get(activityIndex);
+            List<ParsedIntentInfo> intents = activity.getIntents();
+            int intentsSize = intents.size();
+            for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) {
+                ParsedIntentInfo intent = intents.get(intentIndex);
+                if (checkAutoVerify && !intent.getAutoVerify()) {
+                    continue;
+                }
+
+                if (!intent.hasCategory(Intent.CATEGORY_DEFAULT)
+                        || !intent.handlesWebUris(checkAutoVerify)) {
+                    continue;
+                }
+
+                // TODO(b/159952358): There seems to be no way to associate the exact host
+                //  with its scheme, meaning all hosts have to be verified as if they were
+                //  web schemes. This means that given the following:
+                //  <intent-filter android:autoVerify="true">
+                //      ...
+                //      <data android:scheme="https" android:host="one.example.com"/>
+                //      <data android:scheme="https" android:host="two.example.com"/>
+                //      <data android:host="three.example.com"/>
+                //      <data android:scheme="nonWeb" android:host="four.example.com"/>
+                //  </intent-filter>
+                //  The verification agent will be asked to verify four.example.com, which the
+                //  app will probably fail. This can be re-configured to work properly by the
+                //  app developer by declaring a separate intent-filter. This may not be worth
+                //  fixing.
+                int authorityCount = intent.countDataAuthorities();
+                for (int index = 0; index < authorityCount; index++) {
+                    String host = intent.getDataAuthority(index).getHost();
+                    // It's easy to misconfigure autoVerify intent filters, so to avoid
+                    // adding unintended hosts, check if the host is an HTTP domain.
+                    if (Patterns.DOMAIN_NAME.matcher(host).matches()) {
+                        domains.add(host);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
new file mode 100644
index 0000000..af9978b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.PackageUtils;
+import android.util.SparseArray;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
+import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
+import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.util.Arrays;
+
+public class DomainVerificationDebug {
+
+    @NonNull
+    private final DomainVerificationCollector mCollector;
+
+    DomainVerificationDebug(DomainVerificationCollector collector) {
+        mCollector = collector;
+    }
+
+    public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+            @Nullable @UserIdInt Integer userId,
+            @NonNull DomainVerificationService.Connection connection,
+            @NonNull DomainVerificationStateMap<DomainVerificationPkgState> stateMap)
+            throws NameNotFoundException {
+        ArrayMap<String, Integer> reusedMap = new ArrayMap<>();
+        ArraySet<String> reusedSet = new ArraySet<>();
+
+        if (packageName == null) {
+            int size = stateMap.size();
+            for (int index = 0; index < size; index++) {
+                DomainVerificationPkgState pkgState = stateMap.valueAt(index);
+                String pkgName = pkgState.getPackageName();
+                PackageSetting pkgSetting = connection.getPackageSettingLocked(pkgName);
+                if (pkgSetting == null || pkgSetting.getPkg() == null) {
+                    continue;
+                }
+
+                boolean wasHeaderPrinted = printState(writer, pkgState, pkgSetting.getPkg(),
+                        reusedMap, false);
+                printState(writer, pkgState, pkgSetting.getPkg(), userId, reusedSet,
+                        wasHeaderPrinted);
+            }
+        } else {
+            DomainVerificationPkgState pkgState = stateMap.get(packageName);
+            if (pkgState == null) {
+                throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+            }
+
+            PackageSetting pkgSetting = connection.getPackageSettingLocked(packageName);
+            if (pkgSetting == null || pkgSetting.getPkg() == null) {
+                throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+            }
+
+            AndroidPackage pkg = pkgSetting.getPkg();
+            printState(writer, pkgState, pkg, reusedMap, false);
+            printState(writer, pkgState, pkg, userId, reusedSet, true);
+        }
+    }
+
+    boolean printState(@NonNull IndentingPrintWriter writer,
+            @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg,
+            @NonNull ArrayMap<String, Integer> reusedMap, boolean wasHeaderPrinted) {
+        reusedMap.clear();
+        reusedMap.putAll(pkgState.getStateMap());
+
+        ArraySet<String> declaredDomains = mCollector.collectAutoVerifyDomains(pkg);
+        int declaredSize = declaredDomains.size();
+        for (int declaredIndex = 0; declaredIndex < declaredSize; declaredIndex++) {
+            String domain = declaredDomains.valueAt(declaredIndex);
+            reusedMap.putIfAbsent(domain, DomainVerificationState.STATE_NO_RESPONSE);
+        }
+
+        boolean printedHeader = false;
+
+        if (!reusedMap.isEmpty()) {
+            if (!wasHeaderPrinted) {
+                Signature[] signatures = pkg.getSigningDetails().signatures;
+                String signaturesDigest = signatures == null ? null : Arrays.toString(
+                        PackageUtils.computeSignaturesSha256Digests(
+                                pkg.getSigningDetails().signatures));
+
+                writer.println(pkgState.getPackageName() + ":");
+                writer.increaseIndent();
+                writer.println("ID: " + pkgState.getId());
+                writer.println("Signatures: " + signaturesDigest);
+                writer.decreaseIndent();
+                printedHeader = true;
+            }
+
+            writer.increaseIndent();
+            writer.println("Domain verification state:");
+            writer.increaseIndent();
+            int stateSize = reusedMap.size();
+            for (int stateIndex = 0; stateIndex < stateSize; stateIndex++) {
+                String domain = reusedMap.keyAt(stateIndex);
+                Integer state = reusedMap.valueAt(stateIndex);
+                writer.print(domain);
+                writer.print(": ");
+                writer.println(DomainVerificationManager.stateToDebugString(state));
+            }
+            writer.decreaseIndent();
+            writer.decreaseIndent();
+        }
+
+        return printedHeader;
+    }
+
+    void printState(@NonNull IndentingPrintWriter writer,
+            @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg,
+            @Nullable @UserIdInt Integer userId, @NonNull ArraySet<String> reusedSet,
+            boolean wasHeaderPrinted) {
+        if (userId == null) {
+            return;
+        }
+
+        ArraySet<String> allWebDomains = mCollector.collectAllWebDomains(pkg);
+        SparseArray<DomainVerificationUserState> userStates =
+                pkgState.getUserSelectionStates();
+        if (userId == UserHandle.USER_ALL) {
+            int size = userStates.size();
+            if (size == 0) {
+                printState(writer, pkgState, userId, null, reusedSet, allWebDomains,
+                        wasHeaderPrinted);
+            } else {
+                for (int index = 0; index < size; index++) {
+                    DomainVerificationUserState userState = userStates.valueAt(index);
+                    printState(writer, pkgState, userState.getUserId(), userState, reusedSet,
+                            allWebDomains, wasHeaderPrinted);
+                }
+            }
+        } else {
+            DomainVerificationUserState userState = userStates.get(userId);
+            printState(writer, pkgState, userId, userState, reusedSet, allWebDomains,
+                    wasHeaderPrinted);
+        }
+    }
+
+    boolean printState(@NonNull IndentingPrintWriter writer,
+            @NonNull DomainVerificationPkgState pkgState, @UserIdInt int userId,
+            @Nullable DomainVerificationUserState userState, @NonNull ArraySet<String> reusedSet,
+            @NonNull ArraySet<String> allWebDomains, boolean wasHeaderPrinted) {
+        reusedSet.clear();
+        reusedSet.addAll(allWebDomains);
+        if (userState != null) {
+            reusedSet.removeAll(userState.getEnabledHosts());
+        }
+
+        boolean printedHeader = false;
+
+        ArraySet<String> enabledHosts = userState == null ? null : userState.getEnabledHosts();
+        int enabledSize = CollectionUtils.size(enabledHosts);
+        int disabledSize = reusedSet.size();
+        if (enabledSize > 0 || disabledSize > 0) {
+            if (!wasHeaderPrinted) {
+                writer.println(pkgState.getPackageName() + " " + pkgState.getId() + ":");
+                printedHeader = true;
+            }
+
+            boolean isLinkHandlingAllowed = userState == null
+                    || !userState.isDisallowLinkHandling();
+
+            writer.increaseIndent();
+            writer.print("User ");
+            writer.print(userId == UserHandle.USER_ALL ? "all" : userId);
+            writer.println(":");
+            writer.increaseIndent();
+            writer.print("Verification link handling allowed: ");
+            writer.println(isLinkHandlingAllowed);
+            writer.println("Selection state:");
+            writer.increaseIndent();
+
+            if (enabledSize > 0) {
+                writer.println("Enabled:");
+                writer.increaseIndent();
+                for (int enabledIndex = 0; enabledIndex < enabledSize; enabledIndex++) {
+                    //noinspection ConstantConditions
+                    writer.println(enabledHosts.valueAt(enabledIndex));
+                }
+                writer.decreaseIndent();
+            }
+
+            if (disabledSize > 0) {
+                writer.println("Disabled:");
+                writer.increaseIndent();
+                for (int disabledIndex = 0; disabledIndex < disabledSize; disabledIndex++) {
+                    writer.println(reusedSet.valueAt(disabledIndex));
+                }
+                writer.decreaseIndent();
+            }
+
+            writer.decreaseIndent();
+            writer.decreaseIndent();
+            writer.decreaseIndent();
+        }
+
+        return printedHeader;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
new file mode 100644
index 0000000..c521f82
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Process;
+
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+
+public class DomainVerificationEnforcer {
+
+    @NonNull
+    private final Context mContext;
+
+    public DomainVerificationEnforcer(@NonNull Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Enforced when mutating any state from shell or internally in the system process.
+     */
+    public void assertInternal(int callingUid) {
+        switch (callingUid) {
+            case Process.ROOT_UID:
+            case Process.SHELL_UID:
+            case Process.SYSTEM_UID:
+                break;
+            default:
+                throw new SecurityException(
+                        "Caller " + callingUid + " is not allowed to change internal state");
+        }
+    }
+
+    /**
+     * Enforced when retrieving state for a package. The system, the verifier, and anyone approved
+     * to mutate user selections are allowed through.
+     */
+    public void assertApprovedQuerent(int callingUid, @NonNull DomainVerificationProxy proxy) {
+        switch (callingUid) {
+            case Process.ROOT_UID:
+            case Process.SHELL_UID:
+            case Process.SYSTEM_UID:
+                break;
+            default:
+                if (!proxy.isCallerVerifier(callingUid)) {
+                    mContext.enforcePermission(
+                            android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
+                            Binder.getCallingPid(), callingUid,
+                            "Caller " + callingUid
+                                    + " is not allowed to query domain verification state");
+                }
+                break;
+        }
+    }
+
+    /**
+     * Enforced when mutating domain verification state inside an exposed API method.
+     */
+    public void assertApprovedVerifier(int callingUid, @NonNull DomainVerificationProxy proxy)
+            throws SecurityException {
+        boolean isAllowed;
+        switch (callingUid) {
+            case Process.ROOT_UID:
+            case Process.SHELL_UID:
+            case Process.SYSTEM_UID:
+                isAllowed = true;
+                break;
+            default:
+                // TODO(b/159952358): Remove permission check? The component package should
+                //  have been checked when the verifier component was first scanned in PMS.
+                mContext.enforcePermission(
+                        android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
+                        Binder.getCallingPid(), callingUid,
+                        "Caller " + callingUid + " does not hold DOMAIN_VERIFICATION_AGENT");
+                isAllowed = proxy.isCallerVerifier(callingUid);
+                break;
+        }
+
+        if (!isAllowed) {
+            throw new SecurityException("Caller " + callingUid
+                    + " is not the approved domain verification agent, isVerifier = "
+                    + proxy.isCallerVerifier(callingUid));
+        }
+    }
+
+    /**
+     * Enforced when mutating user selection state inside an exposed API method.
+     */
+    public void assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
+            @UserIdInt int targetUserId) throws SecurityException {
+        if (callingUserId != targetUserId) {
+            mContext.enforcePermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS,
+                    Binder.getCallingPid(), callingUid,
+                    "Caller is not allowed to edit other users");
+        }
+
+        mContext.enforcePermission(
+                android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
+                Binder.getCallingPid(), callingUid,
+                "Caller is not allowed to edit user selections");
+    }
+
+    public void callerIsLegacyUserSelector(int callingUid) {
+        mContext.enforcePermission(
+                android.Manifest.permission.SET_PREFERRED_APPLICATIONS,
+                Binder.getCallingPid(), callingUid,
+                "Caller is not allowed to edit user state");
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java
new file mode 100644
index 0000000..c787356
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageManager;
+import android.util.ArrayMap;
+import android.util.SparseIntArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.SettingsXml;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Reads and writes the old {@link android.content.pm.IntentFilterVerificationInfo} so that it can
+ * be migrated in to the new API. Will throw away the state once it's successfully applied so that
+ * eventually there will be no legacy state on the device.
+ *
+ * This attempt is best effort, and if the legacy state is lost that's acceptable. The user setting
+ * in the legacy API may have been set incorrectly because it was never made obvious to the user
+ * what it actually toggled, so there's a strong argument to prevent migration anyways. The user
+ * can just set their preferences again, this time with finer grained control, if the legacy state
+ * gets dropped.
+ */
+public class DomainVerificationLegacySettings {
+
+    public static final String TAG_DOMAIN_VERIFICATIONS_LEGACY = "domain-verifications-legacy";
+    public static final String TAG_USER_STATES = "user-states";
+    public static final String ATTR_PACKAGE_NAME = "packageName";
+    public static final String TAG_USER_STATE = "user-state";
+    public static final String ATTR_USER_ID = "userId";
+    public static final String ATTR_STATE = "state";
+
+    @NonNull
+    private final Object mLock = new Object();
+
+    @NonNull
+    private final ArrayMap<String, LegacyState> mStates = new ArrayMap<>();
+
+    public void add(@NonNull String packageName, @NonNull IntentFilterVerificationInfo info) {
+        synchronized (mLock) {
+            getOrCreateStateLocked(packageName).setInfo(info);
+        }
+    }
+
+    public void add(@NonNull String packageName, @UserIdInt int userId, int state) {
+        synchronized (mLock) {
+            getOrCreateStateLocked(packageName).addUserState(userId, state);
+        }
+    }
+
+    public int getUserState(@NonNull String packageName, @UserIdInt int userId) {
+        synchronized (mLock) {
+            LegacyState state = mStates.get(packageName);
+            if (state != null) {
+                return state.getUserState(userId);
+            }
+        }
+        return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+    }
+
+    @Nullable
+    public SparseIntArray getUserStates(@NonNull String packageName) {
+        synchronized (mLock) {
+            LegacyState state = mStates.get(packageName);
+            if (state != null) {
+                // Yes, this returns outside of the lock, but we assume that retrieval generally
+                // only happens after all adding has concluded from reading settings.
+                return state.getUserStates();
+            }
+        }
+        return null;
+    }
+
+    @Nullable
+    public IntentFilterVerificationInfo remove(@NonNull String packageName) {
+        synchronized (mLock) {
+            LegacyState state = mStates.get(packageName);
+            if (state != null && !state.isAttached()) {
+                state.markAttached();
+                return state.getInfo();
+            }
+        }
+        return null;
+    }
+
+    @GuardedBy("mLock")
+    @NonNull
+    private LegacyState getOrCreateStateLocked(@NonNull String packageName) {
+        LegacyState state = mStates.get(packageName);
+        if (state == null) {
+            state = new LegacyState();
+            mStates.put(packageName, state);
+        }
+
+        return state;
+    }
+
+    public void writeSettings(TypedXmlSerializer xmlSerializer) throws IOException {
+        try (SettingsXml.Serializer serializer = SettingsXml.serializer(xmlSerializer)) {
+            try (SettingsXml.WriteSection ignored =
+                         serializer.startSection(TAG_DOMAIN_VERIFICATIONS_LEGACY)) {
+                synchronized (mLock) {
+                    final int statesSize = mStates.size();
+                    for (int stateIndex = 0; stateIndex < statesSize; stateIndex++) {
+                        final LegacyState state = mStates.valueAt(stateIndex);
+                        final SparseIntArray userStates = state.getUserStates();
+                        if (userStates == null) {
+                            continue;
+                        }
+
+                        final String packageName = mStates.keyAt(stateIndex);
+                        try (SettingsXml.WriteSection userStatesSection =
+                                     serializer.startSection(TAG_USER_STATES)
+                                             .attribute(ATTR_PACKAGE_NAME, packageName)) {
+                            final int userStatesSize = userStates.size();
+                            for (int userStateIndex = 0; userStateIndex < userStatesSize;
+                                    userStateIndex++) {
+                                final int userId = userStates.keyAt(userStateIndex);
+                                final int userState = userStates.valueAt(userStateIndex);
+                                userStatesSection.startSection(TAG_USER_STATE)
+                                        .attribute(ATTR_USER_ID, userId)
+                                        .attribute(ATTR_STATE, userState)
+                                        .finish();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public void readSettings(TypedXmlPullParser xmlParser)
+            throws IOException, XmlPullParserException {
+        final SettingsXml.ChildSection child = SettingsXml.parser(xmlParser).children();
+        while (child.moveToNext()) {
+            if (TAG_USER_STATES.equals(child.getName())) {
+                readUserStates(child);
+            }
+        }
+    }
+
+    private void readUserStates(SettingsXml.ReadSection section) {
+        String packageName = section.getString(ATTR_PACKAGE_NAME);
+        synchronized (mLock) {
+            final LegacyState legacyState = getOrCreateStateLocked(packageName);
+            final SettingsXml.ChildSection child = section.children();
+            while (child.moveToNext()) {
+                if (TAG_USER_STATE.equals(child.getName())) {
+                    readUserState(child, legacyState);
+                }
+            }
+        }
+    }
+
+    private void readUserState(SettingsXml.ReadSection section, LegacyState legacyState) {
+        int userId = section.getInt(ATTR_USER_ID);
+        int state = section.getInt(ATTR_STATE);
+        legacyState.addUserState(userId, state);
+    }
+
+    static class LegacyState {
+        @Nullable
+        private IntentFilterVerificationInfo mInfo;
+
+        @Nullable
+        private SparseIntArray mUserStates;
+
+        private boolean attached;
+
+        @Nullable
+        public IntentFilterVerificationInfo getInfo() {
+            return mInfo;
+        }
+
+        public int getUserState(int userId) {
+            return mUserStates.get(userId,
+                    PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
+        }
+
+        @Nullable
+        public SparseIntArray getUserStates() {
+            return mUserStates;
+        }
+
+        public void setInfo(@NonNull IntentFilterVerificationInfo info) {
+            mInfo = info;
+        }
+
+        public void addUserState(@UserIdInt int userId, int state) {
+            if (mUserStates == null) {
+                mUserStates = new SparseIntArray(1);
+            }
+            mUserStates.put(userId, state);
+        }
+
+        public boolean isAttached() {
+            return attached;
+        }
+
+        public void markAttached() {
+            attached = true;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
new file mode 100644
index 0000000..7ad275a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
+import android.content.Intent;
+import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.UUID;
+
+public interface DomainVerificationManagerInternal extends DomainVerificationManager {
+
+    UUID DISABLED_ID = new UUID(0, 0);
+
+    /**
+     * Generate a new domain set ID to be used for attaching new packages.
+     */
+    @NonNull
+    UUID generateNewId();
+
+    void setConnection(@NonNull Connection connection);
+
+    @NonNull
+    DomainVerificationProxy getProxy();
+
+    /**
+     * Update the proxy implementation that talks to the domain verification agent on device. The
+     * default proxy is a stub that does nothing, and broadcast functionality will only work once a
+     * real implementation is attached.
+     */
+    void setProxy(@NonNull DomainVerificationProxy proxy);
+
+    /**
+     * @see DomainVerificationProxy.BaseConnection#runMessage(int, Object)
+     */
+    boolean runMessage(int messageCode, Object object);
+
+    /**
+     * Restores or creates internal state for the new package. This can either be from scanning a
+     * package at boot, or a truly new installation on the device. It is expected that the {@link
+     * PackageSetting#getDomainSetId()} already be set to the correct value.
+     * <p>
+     * If this is from scan, there should be a pending state that was previous read using {@link
+     * #readSettings(TypedXmlPullParser)}, which will be attached as-is to the package. In this
+     * case, a broadcast will not be sent to the domain verification agent on device, as it is
+     * assumed nothing has changed since the device rebooted.
+     * <p>
+     * If this is a new install, state will be restored from a previous call to {@link
+     * #restoreSettings(TypedXmlPullParser)}, or a new one will be generated. In either case, a
+     * broadcast will be sent to the domain verification agent so it may re-run any verification
+     * logic for the newly associated domains.
+     * <p>
+     * This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
+     * lock. This should never be called from within the domain verification classes themselves.
+     * <p>
+     * This will NOT call {@link #writeSettings(TypedXmlSerializer)}. That must be handled by the
+     * caller.
+     */
+    void addPackage(@NonNull PackageSetting newPkgSetting);
+
+    /**
+     * Migrates verification state from a previous install to a new one. It is expected that the
+     * {@link PackageSetting#getDomainSetId()} already be set to the correct value, usually from
+     * {@link #generateNewId()}. This will preserve {@link #STATE_SUCCESS} domains under the
+     * assumption that the new package will pass the same server side config as the previous
+     * package, as they have matching signatures.
+     * <p>
+     * This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
+     * lock. This should never be called from within the domain verification classes themselves.
+     * <p>
+     * This will NOT call {@link #writeSettings(TypedXmlSerializer)}. That must be handled by the
+     * caller.
+     */
+    void migrateState(@NonNull PackageSetting oldPkgSetting, @NonNull PackageSetting newPkgSetting);
+
+    /**
+     * Serializes the entire internal state. This is equivalent to a full backup of the existing
+     * verification state. This write includes legacy state, as a sibling tag the modern state.
+     */
+    void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException;
+
+    /**
+     * Read back a list of {@link DomainVerificationPkgState}s previously written by {@link
+     * #writeSettings(TypedXmlSerializer)}. Assumes that the
+     * {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS} tag has already been entered.
+     * <p>
+     * This is expected to only be used to re-attach states for packages already known to be on the
+     * device. If restoring from a backup, use {@link #restoreSettings(TypedXmlPullParser)}.
+     */
+    void readSettings(@NonNull TypedXmlPullParser parser)
+            throws IOException, XmlPullParserException;
+
+    /**
+     * Read back data from
+     * {@link DomainVerificationLegacySettings#writeSettings(TypedXmlSerializer)}. Assumes that the
+     * {@link DomainVerificationLegacySettings#TAG_DOMAIN_VERIFICATIONS_LEGACY} tag has already
+     * been entered.
+     */
+    void readLegacySettings(@NonNull TypedXmlPullParser parser)
+            throws IOException, XmlPullParserException;
+
+    /**
+     * Remove all state for the given package.
+     */
+    void clearPackage(@NonNull String packageName);
+
+    /**
+     * Delete all the state for a user. This can be because the user has been removed from the
+     * device, or simply that the state for a user should be deleted.
+     */
+    void clearUser(@UserIdInt int userId);
+
+    /**
+     * Restore a list of {@link DomainVerificationPkgState}s previously written by {@link
+     * #writeSettings(TypedXmlSerializer)}. Assumes that the
+     * {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS}
+     * tag has already been entered.
+     * <p>
+     * This is <b>only</b> for restore, and will override package states, ignoring if their {@link
+     * DomainVerificationInfo#getIdentifier()}s match. It's expected that any restored domains marked
+     * as success verify against the server correctly, although the verification agent may decide to
+     * re-verify them when it gets the chance.
+     */
+    /*
+     * TODO(b/170746586): Figure out how to verify that package signatures match at snapshot time
+     *  and restore time.
+     */
+    void restoreSettings(@NonNull TypedXmlPullParser parser)
+            throws IOException, XmlPullParserException;
+
+    /**
+     * Set aside a legacy {@link IntentFilterVerificationInfo} that will be restored to a pending
+     * {@link DomainVerificationPkgState} once it's added through
+     * {@link #addPackage(PackageSetting)}.
+     */
+    void addLegacySetting(@NonNull String packageName, @NonNull IntentFilterVerificationInfo info);
+
+    /**
+     * Set aside a legacy user selection that will be restored to a pending
+     * {@link DomainVerificationPkgState} once it's added through
+     * {@link #addPackage(PackageSetting)}.
+     */
+    void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);
+
+    /**
+     * Until the legacy APIs are entirely removed, returns the legacy state from the previously
+     * written info stored in {@link com.android.server.pm.Settings}.
+     */
+    int getLegacyState(@NonNull String packageName, @UserIdInt int userId);
+
+    /**
+     * Serialize a legacy setting that wasn't attached yet.
+     * TODO: Does this even matter? Should consider for removal.
+     */
+    void writeLegacySettings(TypedXmlSerializer serializer, String name);
+
+    /**
+     * Print the verification state and user selection state of a package.
+     *
+     * @param packageName the package whose state to change, or all packages if none is specified
+     * @param userId      the specific user to print, or null to skip printing user selection
+     *                    states, supports {@link android.os.UserHandle#USER_ALL}
+     */
+    void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+            @Nullable @UserIdInt Integer userId) throws NameNotFoundException;
+
+    @NonNull
+    DomainVerificationShell getShell();
+
+    @NonNull
+    DomainVerificationCollector getCollector();
+
+    /**
+     * Check if a resolving URI is approved to takeover the domain as the sole resolved target.
+     * This can be because the domain was auto-verified for the package, or if the user manually
+     * chose to enable the domain for the package.
+     */
+    boolean isApprovedForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
+            @UserIdInt int userId);
+
+    /**
+     * @return the domain verification set ID for the given package, or null if the ID is
+     * unavailable
+     */
+    @Nullable
+    UUID getDomainVerificationInfoId(@NonNull String packageName);
+
+    @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
+    void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
+            @NonNull Set<String> domains, int state)
+            throws IllegalArgumentException, NameNotFoundException;
+
+
+    interface Connection {
+
+        /**
+         * Notify that a settings change has been made and that eventually
+         * {@link #writeSettings(TypedXmlSerializer)} should be invoked by the parent.
+         */
+        void scheduleWriteSettings();
+
+        /**
+         * Delegate to {@link Binder#getCallingUid()} to allow mocking in tests.
+         */
+        int getCallingUid();
+
+        /**
+         * Delegate to {@link UserHandle#getCallingUserId()} to allow mocking in tests.
+         */
+        @UserIdInt
+        int getCallingUserId();
+
+        /**
+         * @see DomainVerificationProxy.BaseConnection#schedule(int, java.lang.Object)
+         */
+        void schedule(int code, @Nullable Object object);
+
+        @Nullable
+        PackageSetting getPackageSettingLocked(@NonNull String pkgName);
+
+        @Nullable
+        AndroidPackage getPackageLocked(@NonNull String pkgName);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
new file mode 100644
index 0000000..8aa6337
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
+import android.content.pm.verify.domain.DomainVerificationManagerImpl;
+import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.IDomainVerificationManager;
+import android.os.ServiceSpecificException;
+import android.util.ArraySet;
+
+import java.util.List;
+import java.util.UUID;
+
+class DomainVerificationManagerStub extends IDomainVerificationManager.Stub {
+
+    @NonNull
+    private DomainVerificationService mService;
+
+    DomainVerificationManagerStub(DomainVerificationService service) {
+        mService = service;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getValidVerificationPackageNames() {
+        try {
+            return mService.getValidVerificationPackageNames();
+        } catch (Exception e) {
+            throw rethrow(e);
+        }
+    }
+
+    @Nullable
+    @Override
+    public DomainVerificationInfo getDomainVerificationInfo(String packageName) {
+        try {
+            return mService.getDomainVerificationInfo(packageName);
+        } catch (Exception e) {
+            throw rethrow(e);
+        }
+    }
+
+    @Override
+    public void setDomainVerificationStatus(String domainSetId, List<String> domains,
+            int state) {
+        try {
+            mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
+                            new ArraySet<>(domains), state);
+        } catch (Exception e) {
+            throw rethrow(e);
+        }
+    }
+
+    @Override
+    public void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed,
+            @UserIdInt int userId) {
+        try {
+            mService.setDomainVerificationLinkHandlingAllowed(packageName, allowed, userId);
+        } catch (Exception e) {
+            throw rethrow(e);
+        }
+    }
+
+    @Override
+    public void setDomainVerificationUserSelection(String domainSetId, List<String> domains,
+            boolean enabled, @UserIdInt int userId) {
+        try {
+            mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
+                            new ArraySet<>(domains), enabled, userId);
+        } catch (Exception e) {
+            throw rethrow(e);
+        }
+    }
+
+    @Nullable
+    @Override
+    public DomainVerificationUserSelection getDomainVerificationUserSelection(
+            String packageName, @UserIdInt int userId) {
+        try {
+            return mService.getDomainVerificationUserSelection(packageName, userId);
+        } catch (Exception e) {
+            throw rethrow(e);
+        }
+    }
+
+    private RuntimeException rethrow(Exception exception) throws RuntimeException {
+        if (exception instanceof InvalidDomainSetException) {
+            int packedErrorCode = DomainVerificationManagerImpl.ERROR_INVALID_DOMAIN_SET;
+            packedErrorCode |= ((InvalidDomainSetException) exception).getReason() << 16;
+            return new ServiceSpecificException(packedErrorCode,
+                    ((InvalidDomainSetException) exception).getPackageName());
+        } else if (exception instanceof NameNotFoundException) {
+            return new ServiceSpecificException(
+                    DomainVerificationManagerImpl.ERROR_NAME_NOT_FOUND);
+        } else if (exception instanceof RuntimeException) {
+            return (RuntimeException) exception;
+        } else {
+            return new RuntimeException(exception);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationMessageCodes.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationMessageCodes.java
new file mode 100644
index 0000000..7af78c6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationMessageCodes.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.os.Handler;
+
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+
+/**
+ * Codes that are sent through the {@link PackageManagerService} {@link Handler} and eventually
+ * delegated to {@link DomainVerificationService} and {@link DomainVerificationProxy}.
+ *
+ * These codes are wrapped and thus exclusive to the domain verification APIs. They do not have be
+ * distinct from any of the codes inside {@link PackageManagerService}.
+ */
+public final class DomainVerificationMessageCodes {
+
+    public static final int SEND_REQUEST = 1;
+    public static final int LEGACY_SEND_REQUEST = 2;
+    public static final int LEGACY_ON_INTENT_FILTER_VERIFIED = 3;
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
new file mode 100644
index 0000000..679f948
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.server.pm.SettingsXml;
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
+import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
+import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.UUID;
+
+public class DomainVerificationPersistence {
+
+    private static final String TAG = "DomainVerificationPersistence";
+
+    public static final String TAG_DOMAIN_VERIFICATIONS = "domain-verifications";
+    public static final String TAG_ACTIVE = "active";
+    public static final String TAG_RESTORED = "restored";
+
+    public static final String TAG_PACKAGE_STATE = "package-state";
+    private static final String ATTR_PACKAGE_NAME = "packageName";
+    private static final String ATTR_ID = "id";
+    private static final String ATTR_HAS_AUTO_VERIFY_DOMAINS = "hasAutoVerifyDomains";
+    private static final String TAG_USER_STATES = "user-states";
+
+    public static final String TAG_USER_STATE = "user-state";
+    public static final String ATTR_USER_ID = "userId";
+    public static final String ATTR_DISALLOW_LINK_HANDLING = "disallowLinkHandling";
+    public static final String TAG_ENABLED_HOSTS = "enabled-hosts";
+    public static final String TAG_HOST = "host";
+
+    private static final String TAG_STATE = "state";
+    public static final String TAG_DOMAIN = "domain";
+    public static final String ATTR_NAME = "name";
+    public static final String ATTR_STATE = "state";
+
+    public static void writeToXml(@NonNull TypedXmlSerializer xmlSerializer,
+            @NonNull DomainVerificationStateMap<DomainVerificationPkgState> attached,
+            @NonNull ArrayMap<String, DomainVerificationPkgState> pending,
+            @NonNull ArrayMap<String, DomainVerificationPkgState> restored) throws IOException {
+        try (SettingsXml.Serializer serializer = SettingsXml.serializer(xmlSerializer)) {
+            try (SettingsXml.WriteSection ignored = serializer.startSection(
+                    TAG_DOMAIN_VERIFICATIONS)) {
+                // Both attached and pending states are written to the active set, since both
+                // should be restored when the device reboots or runs a backup. They're merged into
+                // the same list because at read time the distinction isn't relevant. The pending
+                // list should generally be empty at this point anyways.
+                ArraySet<DomainVerificationPkgState> active = new ArraySet<>();
+
+                int attachedSize = attached.size();
+                for (int attachedIndex = 0; attachedIndex < attachedSize; attachedIndex++) {
+                    active.add(attached.valueAt(attachedIndex));
+                }
+
+                int pendingSize = pending.size();
+                for (int pendingIndex = 0; pendingIndex < pendingSize; pendingIndex++) {
+                    active.add(pending.valueAt(pendingIndex));
+                }
+
+                try (SettingsXml.WriteSection activeSection = serializer.startSection(TAG_ACTIVE)) {
+                    writePackageStates(activeSection, active);
+                }
+
+                try (SettingsXml.WriteSection restoredSection = serializer.startSection(
+                        TAG_RESTORED)) {
+                    writePackageStates(restoredSection, restored.values());
+                }
+            }
+        }
+    }
+
+    private static void writePackageStates(@NonNull SettingsXml.WriteSection section,
+            @NonNull Collection<DomainVerificationPkgState> states) throws IOException {
+        if (states.isEmpty()) {
+            return;
+        }
+
+        for (DomainVerificationPkgState state : states) {
+            writePkgStateToXml(section, state);
+        }
+    }
+
+    @NonNull
+    public static ReadResult readFromXml(@NonNull TypedXmlPullParser parentParser)
+            throws IOException, XmlPullParserException {
+        ArrayMap<String, DomainVerificationPkgState> active = new ArrayMap<>();
+        ArrayMap<String, DomainVerificationPkgState> restored = new ArrayMap<>();
+
+        SettingsXml.ChildSection child = SettingsXml.parser(parentParser).children();
+        while (child.moveToNext()) {
+            switch (child.getName()) {
+                case TAG_ACTIVE:
+                    readPackageStates(child, active);
+                    break;
+                case TAG_RESTORED:
+                    readPackageStates(child, restored);
+                    break;
+            }
+        }
+
+        return new ReadResult(active, restored);
+    }
+
+    private static void readPackageStates(@NonNull SettingsXml.ReadSection section,
+            @NonNull ArrayMap<String, DomainVerificationPkgState> map) {
+        SettingsXml.ChildSection child = section.children();
+        while (child.moveToNext(TAG_PACKAGE_STATE)) {
+            DomainVerificationPkgState pkgState = createPkgStateFromXml(child);
+            if (pkgState != null) {
+                // State is unique by package name
+                map.put(pkgState.getPackageName(), pkgState);
+            }
+        }
+    }
+
+    /**
+     * Reads a package state from XML. Assumes the starting {@link #TAG_PACKAGE_STATE} has already
+     * been entered.
+     */
+    @Nullable
+    public static DomainVerificationPkgState createPkgStateFromXml(
+            @NonNull SettingsXml.ReadSection section) {
+        String packageName = section.getString(ATTR_PACKAGE_NAME);
+        String idString = section.getString(ATTR_ID);
+        boolean hasAutoVerifyDomains = section.getBoolean(ATTR_HAS_AUTO_VERIFY_DOMAINS);
+        if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(idString)) {
+            return null;
+        }
+        UUID id = UUID.fromString(idString);
+
+        final ArrayMap<String, Integer> stateMap = new ArrayMap<>();
+        final SparseArray<DomainVerificationUserState> userStates = new SparseArray<>();
+
+        SettingsXml.ChildSection child = section.children();
+        while (child.moveToNext()) {
+            switch (child.getName()) {
+                case TAG_STATE:
+                    readDomainStates(child, stateMap);
+                    break;
+                case TAG_USER_STATES:
+                    readUserStates(child, userStates);
+                    break;
+            }
+        }
+
+        return new DomainVerificationPkgState(packageName, id, hasAutoVerifyDomains, stateMap,
+                userStates);
+    }
+
+    private static void readUserStates(@NonNull SettingsXml.ReadSection section,
+            @NonNull SparseArray<DomainVerificationUserState> userStates) {
+        SettingsXml.ChildSection child = section.children();
+        while (child.moveToNext(TAG_USER_STATE)) {
+            DomainVerificationUserState userState = createUserStateFromXml(child);
+            if (userState != null) {
+                userStates.put(userState.getUserId(), userState);
+            }
+        }
+    }
+
+    private static void readDomainStates(@NonNull SettingsXml.ReadSection stateSection,
+            @NonNull ArrayMap<String, Integer> stateMap) {
+        SettingsXml.ChildSection child = stateSection.children();
+        while (child.moveToNext(TAG_DOMAIN)) {
+            String name = child.getString(ATTR_NAME);
+            int state = child.getInt(ATTR_STATE, DomainVerificationState.STATE_NO_RESPONSE);
+            stateMap.put(name, state);
+        }
+    }
+
+    public static void writePkgStateToXml(@NonNull SettingsXml.WriteSection parentSection,
+            @NonNull DomainVerificationPkgState pkgState) throws IOException {
+        try (SettingsXml.WriteSection ignored =
+                     parentSection.startSection(TAG_PACKAGE_STATE)
+                             .attribute(ATTR_PACKAGE_NAME, pkgState.getPackageName())
+                             .attribute(ATTR_ID, pkgState.getId().toString())
+                             .attribute(ATTR_HAS_AUTO_VERIFY_DOMAINS,
+                                     pkgState.isHasAutoVerifyDomains())) {
+            writeStateMap(parentSection, pkgState.getStateMap());
+            writeUserStates(parentSection, pkgState.getUserSelectionStates());
+        }
+    }
+
+    private static void writeUserStates(@NonNull SettingsXml.WriteSection parentSection,
+            @NonNull SparseArray<DomainVerificationUserState> states) throws IOException {
+        int size = states.size();
+        if (size == 0) {
+            return;
+        }
+
+        try (SettingsXml.WriteSection section = parentSection.startSection(TAG_USER_STATES)) {
+            for (int index = 0; index < size; index++) {
+                writeUserStateToXml(section, states.valueAt(index));
+            }
+        }
+    }
+
+    private static void writeStateMap(@NonNull SettingsXml.WriteSection parentSection,
+            @NonNull ArrayMap<String, Integer> stateMap) throws IOException {
+        if (stateMap.isEmpty()) {
+            return;
+        }
+
+        try (SettingsXml.WriteSection stateSection = parentSection.startSection(TAG_STATE)) {
+            int size = stateMap.size();
+            for (int index = 0; index < size; index++) {
+                stateSection.startSection(TAG_DOMAIN)
+                        .attribute(ATTR_NAME, stateMap.keyAt(index))
+                        .attribute(ATTR_STATE, stateMap.valueAt(index))
+                        .finish();
+            }
+        }
+    }
+
+    /**
+     * Reads a user state from XML. Assumes the starting {@link #TAG_USER_STATE} has already been
+     * entered.
+     */
+    @Nullable
+    public static DomainVerificationUserState createUserStateFromXml(
+            @NonNull SettingsXml.ReadSection section) {
+        int userId = section.getInt(ATTR_USER_ID);
+        if (userId == -1) {
+            return null;
+        }
+
+        boolean disallowLinkHandling = section.getBoolean(ATTR_DISALLOW_LINK_HANDLING);
+        ArraySet<String> enabledHosts = new ArraySet<>();
+
+        SettingsXml.ChildSection child = section.children();
+        while (child.moveToNext(TAG_ENABLED_HOSTS)) {
+            readEnabledHosts(child, enabledHosts);
+        }
+
+        return new DomainVerificationUserState(userId, enabledHosts, disallowLinkHandling);
+    }
+
+    private static void readEnabledHosts(@NonNull SettingsXml.ReadSection section,
+            @NonNull ArraySet<String> enabledHosts) {
+        SettingsXml.ChildSection child = section.children();
+        while (child.moveToNext(TAG_HOST)) {
+            String hostName = child.getString(ATTR_NAME);
+            if (!TextUtils.isEmpty(hostName)) {
+                enabledHosts.add(hostName);
+            }
+        }
+    }
+
+    public static void writeUserStateToXml(@NonNull SettingsXml.WriteSection parentSection,
+            @NonNull DomainVerificationUserState userState) throws IOException {
+        try (SettingsXml.WriteSection section =
+                     parentSection.startSection(TAG_USER_STATE)
+                             .attribute(ATTR_USER_ID, userState.getUserId())
+                             .attribute(ATTR_DISALLOW_LINK_HANDLING,
+                                     userState.isDisallowLinkHandling())) {
+            ArraySet<String> enabledHosts = userState.getEnabledHosts();
+            if (!enabledHosts.isEmpty()) {
+                try (SettingsXml.WriteSection enabledHostsSection =
+                             section.startSection(TAG_ENABLED_HOSTS)) {
+                    int size = enabledHosts.size();
+                    for (int index = 0; index < size; index++) {
+                        enabledHostsSection.startSection(TAG_HOST)
+                                .attribute(ATTR_NAME, enabledHosts.valueAt(index))
+                                .finish();
+                    }
+                }
+            }
+        }
+    }
+
+    public static class ReadResult {
+
+        @NonNull
+        public final ArrayMap<String, DomainVerificationPkgState> active;
+
+        @NonNull
+        public final ArrayMap<String, DomainVerificationPkgState> restored;
+
+        public ReadResult(@NonNull ArrayMap<String, DomainVerificationPkgState> active,
+                @NonNull ArrayMap<String, DomainVerificationPkgState> restored) {
+            this.active = active;
+            this.restored = restored;
+        }
+    }
+}
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
new file mode 100644
index 0000000..53540c8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -0,0 +1,1241 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.IDomainVerificationManager;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.SystemConfig;
+import com.android.server.SystemService;
+import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
+import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
+import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyUnavailable;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+public class DomainVerificationService extends SystemService
+        implements DomainVerificationManagerInternal, DomainVerificationShell.Callback {
+
+    private static final String TAG = "DomainVerificationService";
+
+    public static final boolean DEBUG_APPROVAL = true;
+
+    /**
+     * The new user preference API for verifying domains marked autoVerify=true in
+     * AndroidManifest.xml intent filters is not yet implemented in the current platform preview.
+     * This is anticipated to ship before S releases.
+     *
+     * For now, it is possible to preview the new user preference changes by enabling this
+     * ChangeId and using the <code>adb shell pm set-app-links-user-selection</code> and similar
+     * commands.
+     */
+    @ChangeId
+    @Disabled
+    private static final long SETTINGS_API_V2 = 178111421;
+
+    /**
+     * States that are currently alive and attached to a package. Entries are exclusive with the
+     * state stored in {@link DomainVerificationSettings}, as any pending/restored state should be
+     * immediately attached once its available.
+     * <p>
+     * Generally this should be not accessed directly. Prefer calling {@link
+     * #getAndValidateAttachedLocked(UUID, Set, boolean)}.
+     *
+     * @see #getAndValidateAttachedLocked(UUID, Set, boolean)
+     **/
+    @GuardedBy("mLock")
+    @NonNull
+    private final DomainVerificationStateMap<DomainVerificationPkgState> mAttachedPkgStates =
+            new DomainVerificationStateMap<>();
+
+    /**
+     * Lock for all state reads/writes.
+     */
+    private final Object mLock = new Object();
+
+    @NonNull
+    private Connection mConnection;
+
+    @NonNull
+    private final SystemConfig mSystemConfig;
+
+    @NonNull
+    private final PlatformCompat mPlatformCompat;
+
+    @NonNull
+    private final DomainVerificationSettings mSettings;
+
+    @NonNull
+    private final DomainVerificationCollector mCollector;
+
+    @NonNull
+    private final DomainVerificationEnforcer mEnforcer;
+
+    @NonNull
+    private final DomainVerificationDebug mDebug;
+
+    @NonNull
+    private final DomainVerificationShell mShell;
+
+    @NonNull
+    private final DomainVerificationLegacySettings mLegacySettings;
+
+    @NonNull
+    private final IDomainVerificationManager.Stub mStub = new DomainVerificationManagerStub(this);
+
+    @NonNull
+    private DomainVerificationProxy mProxy = new DomainVerificationProxyUnavailable();
+
+    public DomainVerificationService(@NonNull Context context, @NonNull SystemConfig systemConfig,
+            @NonNull PlatformCompat platformCompat) {
+        super(context);
+        mSystemConfig = systemConfig;
+        mPlatformCompat = platformCompat;
+        mSettings = new DomainVerificationSettings();
+        mCollector = new DomainVerificationCollector(platformCompat, systemConfig);
+        mEnforcer = new DomainVerificationEnforcer(context);
+        mDebug = new DomainVerificationDebug(mCollector);
+        mShell = new DomainVerificationShell(this);
+        mLegacySettings = new DomainVerificationLegacySettings();
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.DOMAIN_VERIFICATION_SERVICE, mStub);
+    }
+
+    @Override
+    public void setConnection(@NonNull Connection connection) {
+        mConnection = connection;
+    }
+
+    @NonNull
+    @Override
+    public DomainVerificationProxy getProxy() {
+        return mProxy;
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        super.onBootPhase(phase);
+        if (phase != SystemService.PHASE_BOOT_COMPLETED || !hasRealVerifier()) {
+            return;
+        }
+
+        verifyPackages(null, false);
+    }
+
+    @Override
+    public void onUserUnlocked(@NonNull TargetUser user) {
+        super.onUserUnlocked(user);
+
+        // Package verification is sent at both boot and user unlock. The latter will allow v1
+        // verification agents to respond to the request, since they will not be directBootAware.
+        // However, ideally v2 implementations are boot aware and can handle the initial boot
+        // broadcast, to start verifying packages as soon as possible. It's possible this causes
+        // unnecessary duplication at device start up, but the implementation is responsible for
+        // de-duplicating.
+        // TODO: This can be improved by checking if the broadcast was received by the
+        //  verification agent in the initial boot broadcast
+        verifyPackages(null, false);
+    }
+
+    @Override
+    public void setProxy(@NonNull DomainVerificationProxy proxy) {
+        mProxy = proxy;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getValidVerificationPackageNames() {
+        mEnforcer.assertApprovedVerifier(mConnection.getCallingUid(), mProxy);
+        List<String> packageNames = new ArrayList<>();
+        synchronized (mLock) {
+            int size = mAttachedPkgStates.size();
+            for (int index = 0; index < size; index++) {
+                DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+                if (pkgState.isHasAutoVerifyDomains()) {
+                    packageNames.add(pkgState.getPackageName());
+                }
+            }
+        }
+        return packageNames;
+    }
+
+    @Nullable
+    @Override
+    public UUID getDomainVerificationInfoId(@NonNull String packageName) {
+        synchronized (mLock) {
+            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+            if (pkgState != null) {
+                return pkgState.getId();
+            } else {
+                return null;
+            }
+        }
+    }
+
+    @Nullable
+    @Override
+    public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
+            throws NameNotFoundException {
+        mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
+        synchronized (mLock) {
+            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+            if (pkgState == null) {
+                return null;
+            }
+
+            AndroidPackage pkg = mConnection.getPackageLocked(packageName);
+            if (pkg == null) {
+                throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+            }
+
+            Map<String, Integer> hostToStateMap = new ArrayMap<>(pkgState.getStateMap());
+
+            // TODO(b/159952358): Should the domain list be cached?
+            ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
+            if (domains.isEmpty()) {
+                return null;
+            }
+
+            int size = domains.size();
+            for (int index = 0; index < size; index++) {
+                hostToStateMap.putIfAbsent(domains.valueAt(index),
+                        DomainVerificationState.STATE_NO_RESPONSE);
+            }
+
+            // TODO(b/159952358): Do not return if no values are editable (all ignored states)?
+            return new DomainVerificationInfo(pkgState.getId(), packageName, hostToStateMap);
+        }
+    }
+
+    @Override
+    public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+            int state) throws InvalidDomainSetException, NameNotFoundException {
+        if (state < DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED) {
+            if (state != DomainVerificationState.STATE_SUCCESS) {
+                throw new IllegalArgumentException(
+                        "Verifier can only set STATE_SUCCESS or codes greater than or equal to "
+                                + "STATE_FIRST_VERIFIER_DEFINED");
+            }
+        }
+
+        setDomainVerificationStatusInternal(mConnection.getCallingUid(), domainSetId, domains,
+                state);
+    }
+
+    @Override
+    public void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
+            @NonNull Set<String> domains, int state)
+            throws InvalidDomainSetException, NameNotFoundException {
+        mEnforcer.assertApprovedVerifier(callingUid, mProxy);
+        synchronized (mLock) {
+            DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
+                    true /* forAutoVerify */);
+            ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+            for (String domain : domains) {
+                Integer previousState = stateMap.get(domain);
+                if (previousState != null
+                        && !DomainVerificationManager.isStateModifiable(previousState)) {
+                    continue;
+                }
+
+                stateMap.put(domain, state);
+            }
+        }
+
+        mConnection.scheduleWriteSettings();
+    }
+
+    @Override
+    public void setDomainVerificationStatusInternal(@Nullable String packageName, int state,
+            @Nullable ArraySet<String> domains) throws NameNotFoundException {
+        mEnforcer.assertInternal(mConnection.getCallingUid());
+
+        switch (state) {
+            case DomainVerificationState.STATE_NO_RESPONSE:
+            case DomainVerificationState.STATE_SUCCESS:
+            case DomainVerificationState.STATE_APPROVED:
+            case DomainVerificationState.STATE_DENIED:
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "State must be one of NO_RESPONSE, SUCCESS, APPROVED, or DENIED");
+        }
+
+        if (packageName == null) {
+            synchronized (mLock) {
+                ArraySet<String> validDomains = new ArraySet<>();
+
+                int size = mAttachedPkgStates.size();
+                for (int index = 0; index < size; index++) {
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+                    String pkgName = pkgState.getPackageName();
+                    PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
+                    if (pkgSetting == null || pkgSetting.getPkg() == null) {
+                        continue;
+                    }
+
+                    AndroidPackage pkg = pkgSetting.getPkg();
+
+                    validDomains.clear();
+
+                    ArraySet<String> autoVerifyDomains = mCollector.collectAutoVerifyDomains(pkg);
+                    if (domains == null) {
+                        validDomains.addAll(autoVerifyDomains);
+                    } else {
+                        validDomains.addAll(domains);
+                        validDomains.retainAll(autoVerifyDomains);
+                    }
+
+                    setDomainVerificationStatusInternal(pkgState, state, validDomains);
+                }
+            }
+        } else {
+            synchronized (mLock) {
+                DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+                if (pkgState == null) {
+                    throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+                }
+
+                PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName);
+                if (pkgSetting == null || pkgSetting.getPkg() == null) {
+                    throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+                }
+
+                AndroidPackage pkg = pkgSetting.getPkg();
+                if (domains == null) {
+                    domains = mCollector.collectAutoVerifyDomains(pkg);
+                } else {
+                    domains.retainAll(mCollector.collectAutoVerifyDomains(pkg));
+                }
+
+                setDomainVerificationStatusInternal(pkgState, state, domains);
+            }
+        }
+
+        mConnection.scheduleWriteSettings();
+    }
+
+    private void setDomainVerificationStatusInternal(@NonNull DomainVerificationPkgState pkgState,
+            int state, @NonNull ArraySet<String> validDomains) {
+        ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+        int size = validDomains.size();
+        for (int index = 0; index < size; index++) {
+            stateMap.put(validDomains.valueAt(index), state);
+        }
+    }
+
+    @Override
+    public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
+            boolean allowed) throws NameNotFoundException {
+        setDomainVerificationLinkHandlingAllowed(packageName, allowed,
+                mConnection.getCallingUserId());
+    }
+
+    public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
+            boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
+        mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+                mConnection.getCallingUserId(), userId);
+        synchronized (mLock) {
+            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+            if (pkgState == null) {
+                throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+            }
+
+            pkgState.getOrCreateUserSelectionState(userId)
+                    .setDisallowLinkHandling(!allowed);
+        }
+
+        mConnection.scheduleWriteSettings();
+    }
+
+    @Override
+    public void setDomainVerificationLinkHandlingAllowedInternal(@Nullable String packageName,
+            boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
+        mEnforcer.assertInternal(mConnection.getCallingUid());
+        if (packageName == null) {
+            synchronized (mLock) {
+                int pkgStateSize = mAttachedPkgStates.size();
+                for (int pkgStateIndex = 0; pkgStateIndex < pkgStateSize; pkgStateIndex++) {
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(pkgStateIndex);
+                    if (userId == UserHandle.USER_ALL) {
+                        SparseArray<DomainVerificationUserState> userStates =
+                                pkgState.getUserSelectionStates();
+                        int userStatesSize = userStates.size();
+                        for (int userStateIndex = 0; userStateIndex < userStatesSize;
+                                userStateIndex++) {
+                            userStates.valueAt(userStateIndex)
+                                    .setDisallowLinkHandling(!allowed);
+                        }
+                    } else {
+                        pkgState.getOrCreateUserSelectionState(userId)
+                                .setDisallowLinkHandling(!allowed);
+                    }
+                }
+
+            }
+        } else {
+            synchronized (mLock) {
+                DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+                if (pkgState == null) {
+                    throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+                }
+
+                pkgState.getOrCreateUserSelectionState(userId)
+                        .setDisallowLinkHandling(!allowed);
+            }
+        }
+
+        mConnection.scheduleWriteSettings();
+    }
+
+    @Override
+    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+            @NonNull Set<String> domains, boolean enabled)
+            throws InvalidDomainSetException, NameNotFoundException {
+        setDomainVerificationUserSelection(domainSetId, domains, enabled,
+                mConnection.getCallingUserId());
+    }
+
+    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+            @NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
+            throws InvalidDomainSetException, NameNotFoundException {
+        mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+                mConnection.getCallingUserId(), userId);
+        synchronized (mLock) {
+            DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
+                    false /* forAutoVerify */);
+            DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
+            if (enabled) {
+                userState.addHosts(domains);
+            } else {
+                userState.removeHosts(domains);
+            }
+        }
+
+        mConnection.scheduleWriteSettings();
+    }
+
+    @Override
+    public void setDomainVerificationUserSelectionInternal(@UserIdInt int userId,
+            @Nullable String packageName, boolean enabled, @NonNull ArraySet<String> domains)
+            throws NameNotFoundException {
+        mEnforcer.assertInternal(mConnection.getCallingUid());
+
+        if (packageName == null) {
+            synchronized (mLock) {
+                Set<String> validDomains = new ArraySet<>();
+
+                int size = mAttachedPkgStates.size();
+                for (int index = 0; index < size; index++) {
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+                    String pkgName = pkgState.getPackageName();
+                    PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
+                    if (pkgSetting == null || pkgSetting.getPkg() == null) {
+                        continue;
+                    }
+
+                    validDomains.clear();
+                    validDomains.addAll(domains);
+
+                    setDomainVerificationUserSelectionInternal(userId, pkgState,
+                            pkgSetting.getPkg(), enabled, validDomains);
+                }
+            }
+        } else {
+            synchronized (mLock) {
+                DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+                if (pkgState == null) {
+                    throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+                }
+
+                PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName);
+                if (pkgSetting == null || pkgSetting.getPkg() == null) {
+                    throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+                }
+
+                setDomainVerificationUserSelectionInternal(userId, pkgState, pkgSetting.getPkg(),
+                        enabled, domains);
+            }
+        }
+
+        mConnection.scheduleWriteSettings();
+    }
+
+    private void setDomainVerificationUserSelectionInternal(int userId,
+            @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg,
+            boolean enabled, Set<String> domains) {
+        domains.retainAll(mCollector.collectAllWebDomains(pkg));
+
+        SparseArray<DomainVerificationUserState> userStates =
+                pkgState.getUserSelectionStates();
+        if (userId == UserHandle.USER_ALL) {
+            int size = userStates.size();
+            for (int index = 0; index < size; index++) {
+                DomainVerificationUserState userState = userStates.valueAt(index);
+                if (enabled) {
+                    userState.addHosts(domains);
+                } else {
+                    userState.removeHosts(domains);
+                }
+            }
+        } else {
+            DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
+            if (enabled) {
+                userState.addHosts(domains);
+            } else {
+                userState.removeHosts(domains);
+            }
+        }
+
+        mConnection.scheduleWriteSettings();
+    }
+
+    @Nullable
+    @Override
+    public DomainVerificationUserSelection getDomainVerificationUserSelection(
+            @NonNull String packageName) throws NameNotFoundException {
+        return getDomainVerificationUserSelection(packageName,
+                mConnection.getCallingUserId());
+    }
+
+    @Nullable
+    @Override
+    public DomainVerificationUserSelection getDomainVerificationUserSelection(
+            @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException {
+        mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+                mConnection.getCallingUserId(), userId);
+        synchronized (mLock) {
+            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+            if (pkgState == null) {
+                return null;
+            }
+
+            AndroidPackage pkg = mConnection.getPackageLocked(packageName);
+            if (pkg == null) {
+                throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+            }
+
+            ArrayMap<String, Boolean> hostToUserSelectionMap = new ArrayMap<>();
+
+            ArraySet<String> domains = mCollector.collectAllWebDomains(pkg);
+            int domainsSize = domains.size();
+            for (int index = 0; index < domainsSize; index++) {
+                hostToUserSelectionMap.put(domains.valueAt(index), false);
+            }
+
+            boolean openVerifiedLinks = false;
+            DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);
+            if (userState != null) {
+                openVerifiedLinks = !userState.isDisallowLinkHandling();
+                ArraySet<String> enabledHosts = userState.getEnabledHosts();
+                int hostsSize = enabledHosts.size();
+                for (int index = 0; index < hostsSize; index++) {
+                    hostToUserSelectionMap.put(enabledHosts.valueAt(index), true);
+                }
+            }
+
+            return new DomainVerificationUserSelection(pkgState.getId(), packageName,
+                    UserHandle.of(userId), openVerifiedLinks, hostToUserSelectionMap);
+        }
+    }
+
+    @NonNull
+    @Override
+    public UUID generateNewId() {
+        // TODO(b/159952358): Domain set ID collisions
+        return UUID.randomUUID();
+    }
+
+    @Override
+    public void migrateState(@NonNull PackageSetting oldPkgSetting,
+            @NonNull PackageSetting newPkgSetting) {
+        String pkgName = newPkgSetting.name;
+        boolean sendBroadcast;
+
+        synchronized (mLock) {
+            UUID oldDomainSetId = oldPkgSetting.getDomainSetId();
+            UUID newDomainSetId = newPkgSetting.getDomainSetId();
+            DomainVerificationPkgState oldPkgState = mAttachedPkgStates.remove(oldDomainSetId);
+
+            AndroidPackage oldPkg = oldPkgSetting.getPkg();
+            AndroidPackage newPkg = newPkgSetting.getPkg();
+
+            ArrayMap<String, Integer> newStateMap = new ArrayMap<>();
+            SparseArray<DomainVerificationUserState> newUserStates = new SparseArray<>();
+
+            if (oldPkgState == null || oldPkg == null || newPkg == null) {
+                // Should be impossible, but to be safe, continue with a new blank state instead
+                Slog.wtf(TAG, "Invalid state nullability old state = " + oldPkgState
+                        + ", old pkgSetting = " + oldPkgSetting
+                        + ", new pkgSetting = " + newPkgSetting
+                        + ", old pkg = " + oldPkg
+                        + ", new pkg = " + newPkg, new Exception());
+
+                DomainVerificationPkgState newPkgState = new DomainVerificationPkgState(
+                        pkgName, newDomainSetId, true, newStateMap, newUserStates);
+                mAttachedPkgStates.put(pkgName, newDomainSetId, newPkgState);
+                return;
+            }
+
+            ArrayMap<String, Integer> oldStateMap = oldPkgState.getStateMap();
+            ArraySet<String> newAutoVerifyDomains = mCollector.collectAutoVerifyDomains(newPkg);
+            int newDomainsSize = newAutoVerifyDomains.size();
+
+            for (int newDomainsIndex = 0; newDomainsIndex < newDomainsSize; newDomainsIndex++) {
+                String domain = newAutoVerifyDomains.valueAt(newDomainsIndex);
+                Integer oldStateInteger = oldStateMap.get(domain);
+                if (oldStateInteger != null) {
+                    int oldState = oldStateInteger;
+                    switch (oldState) {
+                        case DomainVerificationState.STATE_SUCCESS:
+                        case DomainVerificationState.STATE_RESTORED:
+                        case DomainVerificationState.STATE_MIGRATED:
+                            newStateMap.put(domain, oldState);
+                            break;
+                        default:
+                            // In all other cases, the state code is left unset
+                            // (STATE_NO_RESPONSE) to signal to the verification agent that any
+                            // existing error has been cleared and the domain should be
+                            // re-attempted. This makes update of a package a signal to
+                            // re-verify.
+                            break;
+                    }
+                }
+            }
+
+            SparseArray<DomainVerificationUserState> oldUserStates =
+                    oldPkgState.getUserSelectionStates();
+            int oldUserStatesSize = oldUserStates.size();
+            if (oldUserStatesSize > 0) {
+                ArraySet<String> newWebDomains = mCollector.collectAutoVerifyDomains(newPkg);
+                for (int oldUserStatesIndex = 0; oldUserStatesIndex < oldUserStatesSize;
+                        oldUserStatesIndex++) {
+                    int userId = oldUserStates.keyAt(oldUserStatesIndex);
+                    DomainVerificationUserState oldUserState = oldUserStates.valueAt(
+                            oldUserStatesIndex);
+                    ArraySet<String> oldEnabledHosts = oldUserState.getEnabledHosts();
+                    ArraySet<String> newEnabledHosts = new ArraySet<>(oldEnabledHosts);
+                    newEnabledHosts.retainAll(newWebDomains);
+                    DomainVerificationUserState newUserState = new DomainVerificationUserState(
+                            userId, newEnabledHosts, oldUserState.isDisallowLinkHandling());
+                    newUserStates.put(userId, newUserState);
+                }
+            }
+
+            boolean hasAutoVerifyDomains = newDomainsSize > 0;
+            boolean needsBroadcast =
+                    applyImmutableState(pkgName, newStateMap, newAutoVerifyDomains);
+
+            sendBroadcast = hasAutoVerifyDomains && needsBroadcast;
+
+            mAttachedPkgStates.put(pkgName, newDomainSetId, new DomainVerificationPkgState(
+                    pkgName, newDomainSetId, hasAutoVerifyDomains, newStateMap, newUserStates));
+        }
+
+        if (sendBroadcast) {
+            sendBroadcastForPackage(pkgName);
+        }
+    }
+
+    // TODO(b/159952358): Handle valid domainSetIds for PackageSettings with no AndroidPackage
+    @Override
+    public void addPackage(@NonNull PackageSetting newPkgSetting) {
+        // TODO(b/159952358): Optimize packages without any domains. Those wouldn't have to be in
+        //  the state map, but it would require handling the "migration" case where an app either
+        //  gains or loses all domains.
+
+        UUID domainSetId = newPkgSetting.getDomainSetId();
+        String pkgName = newPkgSetting.name;
+
+        boolean sendBroadcast = true;
+
+        DomainVerificationPkgState pkgState;
+        pkgState = mSettings.getPendingState(pkgName);
+        if (pkgState != null) {
+            // Don't send when attaching from pending read, which is usually boot scan. Re-send on
+            // boot is handled in a separate method once all packages are added.
+            sendBroadcast = false;
+        } else {
+            pkgState = mSettings.getRestoredState(pkgName);
+        }
+
+        AndroidPackage pkg = newPkgSetting.getPkg();
+        ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
+        boolean hasAutoVerifyDomains = !domains.isEmpty();
+        boolean isPendingOrRestored = pkgState != null;
+        if (isPendingOrRestored) {
+            pkgState.setId(domainSetId);
+        } else {
+            pkgState = new DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains);
+        }
+
+        boolean needsBroadcast = applyImmutableState(pkgState, domains);
+        if (needsBroadcast && !isPendingOrRestored) {
+            // TODO(b/159952358): Test this behavior
+            // Attempt to preserve user experience by automatically verifying all domains from
+            // legacy state if they were previously approved, or by automatically enabling all
+            // hosts through user selection if legacy state indicates a user previously made the
+            // choice in settings to allow supported links. The domain verification agent should
+            // re-verify these links (set to STATE_MIGRATED) at the next possible opportunity,
+            // and disable them if appropriate.
+            ArraySet<String> webDomains = null;
+
+            SparseIntArray legacyUserStates = mLegacySettings.getUserStates(pkgName);
+            int userStateSize = legacyUserStates == null ? 0 : legacyUserStates.size();
+            for (int index = 0; index < userStateSize; index++) {
+                int userId = legacyUserStates.keyAt(index);
+                int legacyStatus = legacyUserStates.valueAt(index);
+                if (legacyStatus
+                        == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+                    if (webDomains == null) {
+                        webDomains = mCollector.collectAllWebDomains(pkg);
+                    }
+
+                    pkgState.getOrCreateUserSelectionState(userId).addHosts(webDomains);
+                }
+            }
+
+            IntentFilterVerificationInfo legacyInfo = mLegacySettings.remove(pkgName);
+            if (legacyInfo != null
+                    && legacyInfo.getStatus()
+                    == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+                ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+                int domainsSize = domains.size();
+                for (int index = 0; index < domainsSize; index++) {
+                    stateMap.put(domains.valueAt(index), DomainVerificationState.STATE_MIGRATED);
+                }
+            }
+        }
+
+        synchronized (mLock) {
+            mAttachedPkgStates.put(pkgName, domainSetId, pkgState);
+        }
+
+        if (sendBroadcast && hasAutoVerifyDomains) {
+            sendBroadcastForPackage(pkgName);
+        }
+    }
+
+    private boolean applyImmutableState(@NonNull DomainVerificationPkgState pkgState,
+            @NonNull ArraySet<String> autoVerifyDomains) {
+        return applyImmutableState(pkgState.getPackageName(), pkgState.getStateMap(),
+                autoVerifyDomains);
+    }
+
+    /**
+     * Applies any immutable state as the final step when adding or migrating state. Currently only
+     * applies {@link SystemConfig#getLinkedApps()}, which approves all domains for a package.
+     *
+     * @return whether or not a broadcast is necessary for this package
+     */
+    private boolean applyImmutableState(@NonNull String packageName,
+            @NonNull ArrayMap<String, Integer> stateMap,
+            @NonNull ArraySet<String> autoVerifyDomains) {
+        if (mSystemConfig.getLinkedApps().contains(packageName)) {
+            int domainsSize = autoVerifyDomains.size();
+            for (int index = 0; index < domainsSize; index++) {
+                stateMap.put(autoVerifyDomains.valueAt(index),
+                        DomainVerificationState.STATE_SYS_CONFIG);
+            }
+            return false;
+        } else {
+            int size = stateMap.size();
+            for (int index = size - 1; index >= 0; index--) {
+                Integer state = stateMap.valueAt(index);
+                // If no longer marked in SysConfig, demote any previous SysConfig state
+                if (state == DomainVerificationState.STATE_SYS_CONFIG) {
+                    stateMap.removeAt(index);
+                }
+            }
+
+            return true;
+        }
+    }
+
+    @Override
+    public void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException {
+        synchronized (mLock) {
+            mSettings.writeSettings(serializer, mAttachedPkgStates);
+        }
+
+        mLegacySettings.writeSettings(serializer);
+    }
+
+    @Override
+    public void readSettings(@NonNull TypedXmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        synchronized (mLock) {
+            mSettings.readSettings(parser, mAttachedPkgStates);
+        }
+    }
+
+    @Override
+    public void readLegacySettings(@NonNull TypedXmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        mLegacySettings.readSettings(parser);
+    }
+
+    @Override
+    public void restoreSettings(@NonNull TypedXmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        synchronized (mLock) {
+            mSettings.restoreSettings(parser, mAttachedPkgStates);
+        }
+    }
+
+    @Override
+    public void addLegacySetting(@NonNull String packageName,
+            @NonNull IntentFilterVerificationInfo info) {
+        mLegacySettings.add(packageName, info);
+    }
+
+    @Override
+    public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) {
+        mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid());
+        mLegacySettings.add(packageName, userId, state);
+    }
+
+    @Override
+    public int getLegacyState(@NonNull String packageName, @UserIdInt int userId) {
+        return mLegacySettings.getUserState(packageName, userId);
+    }
+
+    @Override
+    public void writeLegacySettings(TypedXmlSerializer serializer, String name) {
+
+    }
+
+    @Override
+    public void clearPackage(@NonNull String packageName) {
+        synchronized (mLock) {
+            mAttachedPkgStates.remove(packageName);
+        }
+
+        mConnection.scheduleWriteSettings();
+    }
+
+    @Override
+    public void clearUser(@UserIdInt int userId) {
+        synchronized (mLock) {
+            int attachedSize = mAttachedPkgStates.size();
+            for (int index = 0; index < attachedSize; index++) {
+                mAttachedPkgStates.valueAt(index).removeUser(userId);
+            }
+
+            mSettings.removeUser(userId);
+        }
+
+        mConnection.scheduleWriteSettings();
+    }
+
+    @Override
+    public boolean runMessage(int messageCode, Object object) {
+        return mProxy.runMessage(messageCode, object);
+    }
+
+    @Override
+    public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+            @Nullable @UserIdInt Integer userId) throws NameNotFoundException {
+        synchronized (mLock) {
+            mDebug.printState(writer, packageName, userId, mConnection, mAttachedPkgStates);
+        }
+    }
+
+    @NonNull
+    @Override
+    public DomainVerificationShell getShell() {
+        return mShell;
+    }
+
+    @NonNull
+    @Override
+    public DomainVerificationCollector getCollector() {
+        return mCollector;
+    }
+
+    private void sendBroadcastForPackage(@NonNull String packageName) {
+        mProxy.sendBroadcastForPackages(Collections.singleton(packageName));
+    }
+
+    private boolean hasRealVerifier() {
+        return !(mProxy instanceof DomainVerificationProxyUnavailable);
+    }
+
+    /**
+     * Validates parameters provided by an external caller. Checks that an ID is still live and that
+     * any provided domains are valid. Should be called at the beginning of each API that takes in a
+     * {@link UUID} domain set ID.
+     */
+    @GuardedBy("mLock")
+    private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId,
+            @NonNull Set<String> domains, boolean forAutoVerify)
+            throws InvalidDomainSetException, NameNotFoundException {
+        if (domainSetId == null) {
+            throw new InvalidDomainSetException(null, null,
+                    InvalidDomainSetException.REASON_ID_NULL);
+        }
+
+        DomainVerificationPkgState pkgState = mAttachedPkgStates.get(domainSetId);
+        if (pkgState == null) {
+            throw new InvalidDomainSetException(domainSetId, null,
+                    InvalidDomainSetException.REASON_ID_INVALID);
+        }
+
+        String pkgName = pkgState.getPackageName();
+        PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
+        if (pkgSetting == null || pkgSetting.getPkg() == null) {
+            throw DomainVerificationUtils.throwPackageUnavailable(pkgName);
+        }
+
+        if (CollectionUtils.isEmpty(domains)) {
+            throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(),
+                    InvalidDomainSetException.REASON_SET_NULL_OR_EMPTY);
+        }
+        AndroidPackage pkg = pkgSetting.getPkg();
+        ArraySet<String> declaredDomains = forAutoVerify
+                ? mCollector.collectAutoVerifyDomains(pkg)
+                : mCollector.collectAllWebDomains(pkg);
+
+        if (domains.retainAll(declaredDomains)) {
+            throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(),
+                    InvalidDomainSetException.REASON_UNKNOWN_DOMAIN);
+        }
+
+        return pkgState;
+    }
+
+    @Override
+    public void verifyPackages(@Nullable List<String> packageNames, boolean reVerify) {
+        mEnforcer.assertInternal(mConnection.getCallingUid());
+        Set<String> packagesToBroadcast = new ArraySet<>();
+
+        if (packageNames == null) {
+            synchronized (mLock) {
+                int pkgStatesSize = mAttachedPkgStates.size();
+                for (int pkgStateIndex = 0; pkgStateIndex < pkgStatesSize; pkgStateIndex++) {
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(pkgStateIndex);
+                    addIfShouldBroadcastLocked(packagesToBroadcast, pkgState, reVerify);
+                }
+            }
+        } else {
+            synchronized (mLock) {
+                int size = packageNames.size();
+                for (int index = 0; index < size; index++) {
+                    String packageName = packageNames.get(index);
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+                    if (pkgState != null) {
+                        addIfShouldBroadcastLocked(packagesToBroadcast, pkgState, reVerify);
+                    }
+                }
+            }
+        }
+
+        if (!packagesToBroadcast.isEmpty()) {
+            mProxy.sendBroadcastForPackages(packagesToBroadcast);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void addIfShouldBroadcastLocked(@NonNull Collection<String> packageNames,
+            @NonNull DomainVerificationPkgState pkgState, boolean reVerify) {
+        if ((reVerify && pkgState.isHasAutoVerifyDomains()) || shouldReBroadcastPackage(pkgState)) {
+            packageNames.add(pkgState.getPackageName());
+        }
+    }
+
+    /**
+     * Determine whether or not a broadcast should be sent at boot for the given {@param pkgState}.
+     * Sends only if the only states recorded are default as decided by {@link
+     * DomainVerificationManager#isStateDefault(int)}.
+     *
+     * If any other state is set, it's assumed that the domain verification agent is aware of the
+     * package and has already scheduled future verification requests.
+     */
+    private boolean shouldReBroadcastPackage(DomainVerificationPkgState pkgState) {
+        if (!pkgState.isHasAutoVerifyDomains()) {
+            return false;
+        }
+
+        ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+        int statesSize = stateMap.size();
+        for (int stateIndex = 0; stateIndex < statesSize; stateIndex++) {
+            Integer state = stateMap.valueAt(stateIndex);
+            if (!DomainVerificationManager.isStateDefault(state)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void clearDomainVerificationState(@Nullable List<String> packageNames) {
+        mEnforcer.assertInternal(mConnection.getCallingUid());
+        synchronized (mLock) {
+            if (packageNames == null) {
+                int size = mAttachedPkgStates.size();
+                for (int index = 0; index < size; index++) {
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+                    String pkgName = pkgState.getPackageName();
+                    PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
+                    if (pkgSetting == null || pkgSetting.getPkg() == null) {
+                        continue;
+                    }
+                    resetDomainState(pkgState, pkgSetting.getPkg());
+                }
+            } else {
+                int size = packageNames.size();
+                for (int index = 0; index < size; index++) {
+                    String pkgName = packageNames.get(index);
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.get(pkgName);
+                    PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
+                    if (pkgSetting == null || pkgSetting.getPkg() == null) {
+                        continue;
+                    }
+                    resetDomainState(pkgState, pkgSetting.getPkg());
+                }
+            }
+        }
+    }
+
+    /**
+     * Reset states that are mutable by the domain verification agent.
+     */
+    private void resetDomainState(@NonNull DomainVerificationPkgState pkgState,
+            @NonNull AndroidPackage pkg) {
+        ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+        int size = stateMap.size();
+        for (int index = size - 1; index >= 0; index--) {
+            Integer state = stateMap.valueAt(index);
+            boolean reset;
+            switch (state) {
+                case DomainVerificationState.STATE_SUCCESS:
+                case DomainVerificationState.STATE_RESTORED:
+                    reset = true;
+                    break;
+                default:
+                    reset = state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+                    break;
+            }
+
+            if (reset) {
+                stateMap.removeAt(index);
+            }
+        }
+
+        applyImmutableState(pkgState, mCollector.collectAutoVerifyDomains(pkg));
+    }
+
+    @Override
+    public void clearUserSelections(@Nullable List<String> packageNames, @UserIdInt int userId) {
+        mEnforcer.assertInternal(mConnection.getCallingUid());
+        synchronized (mLock) {
+            if (packageNames == null) {
+                int size = mAttachedPkgStates.size();
+                for (int index = 0; index < size; index++) {
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+                    if (userId == UserHandle.USER_ALL) {
+                        pkgState.removeAllUsers();
+                    } else {
+                        pkgState.removeUser(userId);
+                    }
+                }
+            } else {
+                int size = packageNames.size();
+                for (int index = 0; index < size; index++) {
+                    String pkgName = packageNames.get(index);
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.get(pkgName);
+                    if (userId == UserHandle.USER_ALL) {
+                        pkgState.removeAllUsers();
+                    } else {
+                        pkgState.removeUser(userId);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean isApprovedForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
+            @UserIdInt int userId) {
+        String packageName = pkgSetting.name;
+        if (!DomainVerificationUtils.isDomainVerificationIntent(intent)) {
+            if (DEBUG_APPROVAL) {
+                debugApproval(packageName, intent, userId, false, "not valid intent");
+            }
+            return false;
+        }
+
+        String host = intent.getData().getHost();
+        final AndroidPackage pkg = pkgSetting.getPkg();
+
+        // Should never be null, but if it is, skip this and assume that v2 is enabled
+        if (pkg != null) {
+            // To allow an instant app to immediately open domains after being installed by the
+            // user, auto approve them for any declared autoVerify domains.
+            if (pkgSetting.getInstantApp(userId)
+                    && mCollector.collectAutoVerifyDomains(pkg).contains(host)) {
+                return true;
+            }
+
+            if (!DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, SETTINGS_API_V2)) {
+                int legacyState = mLegacySettings.getUserState(packageName, userId);
+                switch (legacyState) {
+                    case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+                        // If nothing specifically set, assume v2 rules
+                        break;
+                    case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+                    case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
+                    case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK:
+                        // With v2 split into 2 lists, always and undefined, the concept of whether
+                        // or not to ask is irrelevant. Assume the user wants this application to
+                        // open the domain.
+                        return true;
+                    case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
+                        // Never has the same semantics are before
+                        return false;
+                }
+            }
+        }
+
+        synchronized (mLock) {
+            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+            if (pkgState == null) {
+                if (DEBUG_APPROVAL) {
+                    debugApproval(packageName, intent, userId, false, "pkgState unavailable");
+                }
+                return false;
+            }
+
+            ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+            DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);
+
+            // Only allow autoVerify approval if the user hasn't disabled it
+            if (userState == null || !userState.isDisallowLinkHandling()) {
+                // Check if the exact host matches
+                Integer state = stateMap.get(host);
+                if (state != null && DomainVerificationManager.isStateVerified(state)) {
+                    if (DEBUG_APPROVAL) {
+                        debugApproval(packageName, intent, userId, true, "host verified exactly");
+                    }
+                    return true;
+                }
+
+                // Otherwise see if the host matches a verified domain by wildcard
+                int stateMapSize = stateMap.size();
+                for (int index = 0; index < stateMapSize; index++) {
+                    if (!DomainVerificationManager.isStateVerified(stateMap.valueAt(index))) {
+                        continue;
+                    }
+
+                    String domain = stateMap.keyAt(index);
+                    if (domain.startsWith("*.") && host.endsWith(domain.substring(2))) {
+                        if (DEBUG_APPROVAL) {
+                            debugApproval(packageName, intent, userId, true,
+                                    "host verified by wildcard");
+                        }
+                        return true;
+                    }
+                }
+            }
+
+            // Check user state if available
+            if (userState == null) {
+                if (DEBUG_APPROVAL) {
+                    debugApproval(packageName, intent, userId, false, "userState unavailable");
+                }
+                return false;
+            }
+
+            // See if the user has approved the exact host
+            ArraySet<String> enabledHosts = userState.getEnabledHosts();
+            if (enabledHosts.contains(host)) {
+                if (DEBUG_APPROVAL) {
+                    debugApproval(packageName, intent, userId, true,
+                            "host enabled by user exactly");
+                }
+                return true;
+            }
+
+            // See if the host matches a user selection by wildcard
+            int enabledHostsSize = enabledHosts.size();
+            for (int index = 0; index < enabledHostsSize; index++) {
+                String domain = enabledHosts.valueAt(index);
+                if (domain.startsWith("*.") && host.endsWith(domain.substring(2))) {
+                    if (DEBUG_APPROVAL) {
+                        debugApproval(packageName, intent, userId, true,
+                                "host enabled by user through wildcard");
+                    }
+                    return true;
+                }
+            }
+
+            if (DEBUG_APPROVAL) {
+                debugApproval(packageName, intent, userId, false, "not approved");
+            }
+            return false;
+        }
+    }
+
+    private void debugApproval(@NonNull String packageName, @NonNull Intent intent,
+            @UserIdInt int userId, boolean approved, @NonNull String reason) {
+        String approvalString = approved ? "approved" : "denied";
+        Slog.d(TAG + "Approval", packageName + " was " + approvalString + " for " + intent
+                + " for user " + userId + ": " + reason);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
new file mode 100644
index 0000000..073967e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
+import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
+import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class DomainVerificationSettings {
+
+    /**
+     * States read from disk that have yet to attach to a package, but are expected to, generally in
+     * the context of scanning packages already on disk. This is expected to be empty once the boot
+     * package scan completes.
+     **/
+    @GuardedBy("mLock")
+    @NonNull
+    private final ArrayMap<String, DomainVerificationPkgState> mPendingPkgStates = new ArrayMap<>();
+
+    /**
+     * States from restore that have yet to attach to a package. These are special in that their IDs
+     * are dropped when the package is installed/otherwise becomes available, because the ID will
+     * not match if the data is restored from a different device install.
+     * <p>
+     * If multiple restore calls come in and they overlap, the latest entry added for a package name
+     * will be taken, dropping any previous versions.
+     **/
+    @GuardedBy("mLock")
+    @NonNull
+    private final ArrayMap<String, DomainVerificationPkgState> mRestoredPkgStates =
+            new ArrayMap<>();
+
+    /**
+     * Lock for all state reads/writes.
+     */
+    private final Object mLock = new Object();
+
+
+    public void writeSettings(@NonNull TypedXmlSerializer xmlSerializer,
+            @NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState)
+            throws IOException {
+        synchronized (mLock) {
+            DomainVerificationPersistence.writeToXml(xmlSerializer, liveState,
+                    mPendingPkgStates, mRestoredPkgStates);
+        }
+    }
+
+    /**
+     * Parses a previously stored set of states and merges them with {@param liveState}, directly
+     * mutating the values. This is intended for reading settings written by {@link
+     * #writeSettings(TypedXmlSerializer, DomainVerificationStateMap)} on the same device setup.
+     */
+    public void readSettings(@NonNull TypedXmlPullParser parser,
+            @NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState)
+            throws IOException, XmlPullParserException {
+        DomainVerificationPersistence.ReadResult result =
+                DomainVerificationPersistence.readFromXml(parser);
+        ArrayMap<String, DomainVerificationPkgState> active = result.active;
+        ArrayMap<String, DomainVerificationPkgState> restored = result.restored;
+
+        synchronized (mLock) {
+            int activeSize = active.size();
+            for (int activeIndex = 0; activeIndex < activeSize; activeIndex++) {
+                DomainVerificationPkgState pkgState = active.valueAt(activeIndex);
+                String pkgName = pkgState.getPackageName();
+                DomainVerificationPkgState existingState = liveState.get(pkgName);
+                if (existingState != null) {
+                    // This branch should never be possible. Settings should be read from disk
+                    // before any states are attached. But just in case, handle it.
+                    if (!existingState.getId().equals(pkgState.getId())) {
+                        mergePkgState(existingState, pkgState);
+                    }
+                } else {
+                    mPendingPkgStates.put(pkgName, pkgState);
+                }
+            }
+
+            int restoredSize = restored.size();
+            for (int restoredIndex = 0; restoredIndex < restoredSize; restoredIndex++) {
+                DomainVerificationPkgState pkgState = restored.valueAt(restoredIndex);
+                mRestoredPkgStates.put(pkgState.getPackageName(), pkgState);
+            }
+        }
+    }
+
+    /**
+     * Parses a previously stored set of states and merges them with {@param liveState}, directly
+     * mutating the values. This is intended for restoration across device setups.
+     */
+    public void restoreSettings(@NonNull TypedXmlPullParser parser,
+            @NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState)
+            throws IOException, XmlPullParserException {
+        // TODO(b/170746586): Restoration assumes user IDs match, which is probably not the case on
+        //  a new device.
+
+        DomainVerificationPersistence.ReadResult result =
+                DomainVerificationPersistence.readFromXml(parser);
+
+        // When restoring settings, both active and previously restored are merged, since they
+        // should both go into the newly restored data. Active is added on top of restored just
+        // in case a duplicate is found. Active should be preferred.
+        ArrayMap<String, DomainVerificationPkgState> stateList = result.restored;
+        stateList.putAll(result.active);
+
+        synchronized (mLock) {
+            for (int stateIndex = 0; stateIndex < stateList.size(); stateIndex++) {
+                DomainVerificationPkgState newState = stateList.valueAt(stateIndex);
+                String pkgName = newState.getPackageName();
+                DomainVerificationPkgState existingState = liveState.get(pkgName);
+                if (existingState == null) {
+                    existingState = mPendingPkgStates.get(pkgName);
+                }
+                if (existingState == null) {
+                    existingState = mRestoredPkgStates.get(pkgName);
+                }
+
+                if (existingState != null) {
+                    mergePkgState(existingState, newState);
+                } else {
+                    // If there's no existing state, that means the new state has to be transformed
+                    // in preparation for attaching to brand new package that may eventually be
+                    // installed. This means coercing STATE_SUCCESS and STATE_RESTORED to
+                    // STATE_RESTORED and dropping everything else, the same logic that
+                    // mergePkgState runs, without the merge part.
+                    ArrayMap<String, Integer> stateMap = newState.getStateMap();
+                    int size = stateMap.size();
+                    for (int index = 0; index < size; index++) {
+                        Integer stateInteger = stateMap.valueAt(index);
+                        if (stateInteger != null) {
+                            int state = stateInteger;
+                            if (state == DomainVerificationState.STATE_SUCCESS
+                                    || state == DomainVerificationState.STATE_RESTORED) {
+                                stateMap.setValueAt(index, state);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Merges a newly restored state with existing state. This should only be called for restore,
+     * when the IDs aren't required to match.
+     * <p>
+     * If the existing state for a domain is
+     * {@link DomainVerificationState#STATE_NO_RESPONSE}, then it will be overridden with
+     * {@link DomainVerificationState#STATE_RESTORED} if the restored state is
+     * {@link DomainVerificationState#STATE_SUCCESS} or
+     * {@link DomainVerificationState#STATE_RESTORED}.
+     * <p>
+     * Otherwise the existing state is preserved, assuming any system rules, success state, or
+     * specific error codes are fresher than the restored state. Essentially state is only restored
+     * to grant additional verifications to an app.
+     * <p>
+     * For user selection state, presence in either state will be considered an enabled host. NOTE:
+     * only {@link UserHandle#USER_SYSTEM} is merged. There is no restore path in place for
+     * multiple users.
+     * <p>
+     * TODO(b/170746586): Figure out the restore path for multiple users
+     * <p>
+     * This will mutate {@param oldState} to contain the merged state.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public static void mergePkgState(@NonNull DomainVerificationPkgState oldState,
+            @NonNull DomainVerificationPkgState newState) {
+        ArrayMap<String, Integer> oldStateMap = oldState.getStateMap();
+        ArrayMap<String, Integer> newStateMap = newState.getStateMap();
+        int size = newStateMap.size();
+        for (int index = 0; index < size; index++) {
+            String domain = newStateMap.keyAt(index);
+            Integer newStateCode = newStateMap.valueAt(index);
+            Integer oldStateCodeInteger = oldStateMap.get(domain);
+            if (oldStateCodeInteger == null) {
+                // Cannot add domains to an app
+                continue;
+            }
+
+            int oldStateCode = oldStateCodeInteger;
+            if (oldStateCode == DomainVerificationState.STATE_NO_RESPONSE) {
+                if (newStateCode == DomainVerificationState.STATE_SUCCESS
+                        || newStateCode == DomainVerificationState.STATE_RESTORED) {
+                    oldStateMap.put(domain, DomainVerificationState.STATE_RESTORED);
+                }
+            }
+        }
+
+        SparseArray<DomainVerificationUserState> oldSelectionStates =
+                oldState.getUserSelectionStates();
+
+        SparseArray<DomainVerificationUserState> newSelectionStates =
+                newState.getUserSelectionStates();
+
+        DomainVerificationUserState newUserState = newSelectionStates.get(UserHandle.USER_SYSTEM);
+        if (newUserState != null) {
+            ArraySet<String> newEnabledHosts = newUserState.getEnabledHosts();
+            DomainVerificationUserState oldUserState =
+                    oldSelectionStates.get(UserHandle.USER_SYSTEM);
+
+            boolean disallowLinkHandling = newUserState.isDisallowLinkHandling();
+            if (oldUserState == null) {
+                oldUserState = new DomainVerificationUserState(UserHandle.USER_SYSTEM,
+                        newEnabledHosts, disallowLinkHandling);
+                oldSelectionStates.put(UserHandle.USER_SYSTEM, oldUserState);
+            } else {
+                oldUserState.addHosts(newEnabledHosts)
+                        .setDisallowLinkHandling(disallowLinkHandling);
+            }
+        }
+    }
+
+    public void removeUser(@UserIdInt int userId) {
+        int pendingSize = mPendingPkgStates.size();
+        for (int index = 0; index < pendingSize; index++) {
+            mPendingPkgStates.valueAt(index).removeUser(userId);
+        }
+
+        // TODO(b/170746586): Restored assumes user IDs match, which is probably not the case
+        //  on a new device
+        int restoredSize = mRestoredPkgStates.size();
+        for (int index = 0; index < restoredSize; index++) {
+            mRestoredPkgStates.valueAt(index).removeUser(userId);
+        }
+    }
+
+    @Nullable
+    public DomainVerificationPkgState getPendingState(@NonNull String pkgName) {
+        synchronized (mLock) {
+            return mPendingPkgStates.get(pkgName);
+        }
+    }
+
+    @Nullable
+    public DomainVerificationPkgState getRestoredState(@NonNull String pkgName) {
+        synchronized (mLock) {
+            return mRestoredPkgStates.get(pkgName);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
new file mode 100644
index 0000000..7f9e75a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+
+import com.android.modules.utils.BasicShellCommandHandler;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class DomainVerificationShell {
+
+    @NonNull
+    private final Callback mCallback;
+
+    public DomainVerificationShell(@NonNull Callback callback) {
+        mCallback = callback;
+    }
+
+    public void printHelp(@NonNull PrintWriter pw) {
+        pw.println("  get-app-links [--user <USER_ID>] [<PACKAGE>]");
+        pw.println("    Prints the domain verification state for the given package, or for all");
+        pw.println("    packages if none is specified.");
+        pw.println("      --user <USER_ID>: include user selections (includes all domains, not");
+        pw.println("        just autoVerify ones)");
+        pw.println("  reset-app-links [--user <USER_ID>] [<PACKAGE>]");
+        pw.println("    Resets domain verification state for the given package, or for all");
+        pw.println("    packages if none is specified.");
+        pw.println("      --user <USER_ID>: clear user selection state instead; note this means");
+        pw.println("        domain verification state will NOT be cleared");
+        pw.println("      <PACKAGE>: the package to reset, or \"all\" to reset all packages");
+        pw.println("  verify-app-links [--re-verify] [<PACKAGE>]");
+        pw.println("    Broadcasts a verification request for the given package, or for all");
+        pw.println("    packages if none is specified. Only sends if the package has previously");
+        pw.println("    not recorded a response.");
+        pw.println("      --re-verify: send even if the package has recorded a response");
+        pw.println("  set-app-links [--package <PACKAGE>] <STATE> <DOMAINS>...");
+        pw.println("    Manually set the state of a domain for a package. The domain must be");
+        pw.println("    declared by the package as autoVerify for this to work. This command");
+        pw.println("    will not report a failure for domains that could not be applied.");
+        pw.println("      --package <PACKAGE>: the package to set, or \"all\" to set all packages");
+        pw.println("      <STATE>: the code to set the domains to, valid values are:");
+        pw.println("        STATE_NO_RESPONSE (0): reset as if no response was ever recorded.");
+        pw.println("        STATE_SUCCESS (1): treat domain as successfully verified by domain.");
+        pw.println("          verification agent. Note that the domain verification agent can");
+        pw.println("          override this.");
+        pw.println("        STATE_APPROVED (2): treat domain as always approved, preventing the");
+        pw.println("           domain verification agent from changing it.");
+        pw.println("        STATE_DENIED (3): treat domain as always denied, preveting the domain");
+        pw.println("          verification agent from changing it.");
+        pw.println("      <DOMAINS>: space separated list of domains to change, or \"all\" to");
+        pw.println("        change every domain.");
+        pw.println("  set-app-links-user-selection --user <USER_ID> [--package <PACKAGE>]");
+        pw.println("      <ENABLED> <DOMAINS>...");
+        pw.println("    Manually set the state of a host user selection for a package. The domain");
+        pw.println("    must be declared by the package for this to work. This command will not");
+        pw.println("    report a failure for domains that could not be applied.");
+        pw.println("      --user <USER_ID>: the user to change selections for");
+        pw.println("      --package <PACKAGE>: the package to set, or \"all\" to set all packages");
+        pw.println("      <ENABLED>: whether or not to approve the domain");
+        pw.println("      <DOMAINS>: space separated list of domains to change, or \"all\" to");
+        pw.println("        change every domain.");
+        pw.println("  set-app-links-allowed --user <USER_ID> [--package <PACKAGE>] <ALLOWED>");
+        pw.println("      <ENABLED> <DOMAINS>...");
+        pw.println("    Toggle the auto verified link handling setting for a package.");
+        pw.println("      --user <USER_ID>: the user to change selections for");
+        pw.println("      --package <PACKAGE>: the package to set, or \"all\" to set all packages");
+        pw.println("        packages will be reset if no one package is specified.");
+        pw.println("      <ALLOWED>: true to allow the package to open auto verified links, false");
+        pw.println("        to disable");
+    }
+
+    /**
+     * Run a shell/debugging command.
+     *
+     * @return null if the command is unhandled, true if the command succeeded, false if it failed
+     */
+    public Boolean runCommand(@NonNull BasicShellCommandHandler commandHandler,
+            @NonNull String command) {
+        switch (command) {
+            case "get-app-links":
+                return runGetAppLinks(commandHandler);
+            case "reset-app-links":
+                return runResetAppLinks(commandHandler);
+            case "verify-app-links":
+                return runVerifyAppLinks(commandHandler);
+            case "set-app-links":
+                return runSetAppLinks(commandHandler);
+            case "set-app-links-user-selection":
+                return runSetAppLinksUserSelection(commandHandler);
+            case "set-app-links-allowed":
+                return runSetAppLinksAllowed(commandHandler);
+        }
+
+        return null;
+    }
+
+
+    // pm set-app-links [--package <PACKAGE>] <STATE> <DOMAINS>...
+    private boolean runSetAppLinks(@NonNull BasicShellCommandHandler commandHandler) {
+        String packageName = null;
+
+        String option;
+        while ((option = commandHandler.getNextOption()) != null) {
+            if (option.equals("--package")) {
+                packageName = commandHandler.getNextArgRequired();
+            } else {
+                commandHandler.getErrPrintWriter().println("Error: unknown option: " + option);
+                return false;
+            }
+        }
+
+        if (TextUtils.isEmpty(packageName)) {
+            commandHandler.getErrPrintWriter().println("Error: no package specified");
+            return false;
+        } else if (packageName.equalsIgnoreCase("all")) {
+            packageName = null;
+        }
+
+        String state = commandHandler.getNextArgRequired();
+        int stateInt;
+        switch (state) {
+            case "STATE_NO_RESPONSE":
+            case "0":
+                stateInt = DomainVerificationState.STATE_NO_RESPONSE;
+                break;
+            case "STATE_SUCCESS":
+            case "1":
+                stateInt = DomainVerificationState.STATE_SUCCESS;
+                break;
+            case "STATE_APPROVED":
+            case "2":
+                stateInt = DomainVerificationState.STATE_APPROVED;
+                break;
+            case "STATE_DENIED":
+            case "3":
+                stateInt = DomainVerificationState.STATE_DENIED;
+                break;
+            default:
+                commandHandler.getErrPrintWriter().println("Invalid state option: " + state);
+                return false;
+        }
+
+        ArraySet<String> domains = new ArraySet<>(getRemainingArgs(commandHandler));
+        if (domains.isEmpty()) {
+            commandHandler.getErrPrintWriter().println("No domains specified");
+            return false;
+        }
+
+        if (domains.size() == 1 && domains.contains("all")) {
+            domains = null;
+        }
+
+        try {
+            mCallback.setDomainVerificationStatusInternal(packageName, stateInt,
+                    domains);
+        } catch (NameNotFoundException e) {
+            commandHandler.getErrPrintWriter().println("Package not found: " + packageName);
+            return false;
+        }
+        return true;
+    }
+
+    // pm set-app-links-user-selection --user <USER_ID> [--package <PACKAGE>] <ENABLED> <DOMAINS>...
+    private boolean runSetAppLinksUserSelection(@NonNull BasicShellCommandHandler commandHandler) {
+        Integer userId = null;
+        String packageName = null;
+
+        String option;
+        while ((option = commandHandler.getNextOption()) != null) {
+            switch (option) {
+                case "--user":
+                    userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
+                    break;
+                case "--package":
+                    packageName = commandHandler.getNextArgRequired();
+                    break;
+                default:
+                    commandHandler.getErrPrintWriter().println("Error: unknown option: " + option);
+                    return false;
+            }
+        }
+
+        if (TextUtils.isEmpty(packageName)) {
+            commandHandler.getErrPrintWriter().println("Error: no package specified");
+            return false;
+        } else if (packageName.equalsIgnoreCase("all")) {
+            packageName = null;
+        }
+
+        if (userId == null) {
+            commandHandler.getErrPrintWriter().println("Error: User ID not specified");
+            return false;
+        }
+
+        userId = translateUserId(userId, "runSetAppLinksUserSelection");
+
+        String enabledString = commandHandler.getNextArgRequired();
+
+        // Manually ensure that "true" and "false" are the only options, to ensure a domain isn't
+        // accidentally parsed as a boolean
+        boolean enabled;
+        switch (enabledString) {
+            case "true":
+                enabled = true;
+                break;
+            case "false":
+                enabled = false;
+                break;
+            default:
+                commandHandler.getErrPrintWriter().println(
+                        "Invalid enabled param: " + enabledString);
+                return false;
+        }
+
+        ArraySet<String> domains = new ArraySet<>(getRemainingArgs(commandHandler));
+        if (domains.isEmpty()) {
+            commandHandler.getErrPrintWriter().println("No domains specified");
+            return false;
+        }
+
+        try {
+            mCallback.setDomainVerificationUserSelectionInternal(userId,
+                    packageName, enabled, domains);
+        } catch (NameNotFoundException e) {
+            commandHandler.getErrPrintWriter().println("Package not found: " + packageName);
+            return false;
+        }
+        return true;
+    }
+
+    // pm get-app-links [--user <USER_ID>] [<PACKAGE>]
+    private boolean runGetAppLinks(@NonNull BasicShellCommandHandler commandHandler) {
+        Integer userId = null;
+
+        String option;
+        while ((option = commandHandler.getNextOption()) != null) {
+            if (option.equals("--user")) {
+                userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
+            } else {
+                commandHandler.getErrPrintWriter().println("Error: unknown option: " + option);
+                return false;
+            }
+        }
+
+        userId = userId == null ? null : translateUserId(userId, "runGetAppLinks");
+
+        String packageName = commandHandler.getNextArg();
+
+        try (IndentingPrintWriter writer = new IndentingPrintWriter(
+                commandHandler.getOutPrintWriter(), /* singleIndent */ "  ", /* wrapLength */
+                120)) {
+            writer.increaseIndent();
+            try {
+                mCallback.printState(writer, packageName, userId);
+            } catch (NameNotFoundException e) {
+                commandHandler.getErrPrintWriter().println(
+                        "Error: package " + packageName + " unavailable");
+                return false;
+            }
+            writer.decreaseIndent();
+            return true;
+        }
+    }
+
+    // pm reset-app-links [--user USER_ID] [<PACKAGE>]
+    private boolean runResetAppLinks(@NonNull BasicShellCommandHandler commandHandler) {
+        Integer userId = null;
+
+        String option;
+        while ((option = commandHandler.getNextOption()) != null) {
+            if (option.equals("--user")) {
+                userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
+            } else {
+                commandHandler.getErrPrintWriter().println("Error: unknown option: " + option);
+                return false;
+            }
+        }
+
+        userId = userId == null ? null : translateUserId(userId, "runResetAppLinks");
+
+        List<String> packageNames;
+        String pkgNameArg = commandHandler.peekNextArg();
+        if (TextUtils.isEmpty(pkgNameArg)) {
+            commandHandler.getErrPrintWriter().println("Error: no package specified");
+            return false;
+        } else if (pkgNameArg.equalsIgnoreCase("all")) {
+            packageNames = null;
+        } else {
+            packageNames = Arrays.asList(commandHandler.peekRemainingArgs());
+        }
+
+        if (userId != null) {
+            mCallback.clearUserSelections(packageNames, userId);
+        } else {
+            mCallback.clearDomainVerificationState(packageNames);
+        }
+
+        return true;
+    }
+
+    // pm verify-app-links [--re-verify] [<PACKAGE>]
+    private boolean runVerifyAppLinks(@NonNull BasicShellCommandHandler commandHandler) {
+        boolean reVerify = false;
+        String option;
+        while ((option = commandHandler.getNextOption()) != null) {
+            if (option.equals("--re-verify")) {
+                reVerify = true;
+            } else {
+                commandHandler.getErrPrintWriter().println("Error: unknown option: " + option);
+                return false;
+            }
+        }
+
+        List<String> packageNames = null;
+        String pkgNameArg = commandHandler.getNextArg();
+        if (!TextUtils.isEmpty(pkgNameArg)) {
+            packageNames = Collections.singletonList(pkgNameArg);
+        }
+
+        mCallback.verifyPackages(packageNames, reVerify);
+
+        return true;
+    }
+
+    // pm set-app-links-allowed [--package <PACKAGE>] [--user <USER_ID>] <ALLOWED>
+    private boolean runSetAppLinksAllowed(@NonNull BasicShellCommandHandler commandHandler) {
+        String packageName = null;
+        Integer userId = null;
+        Boolean allowed = null;
+        String option;
+        while ((option = commandHandler.getNextOption()) != null) {
+            if (option.equals("--package")) {
+                packageName = commandHandler.getNextArgRequired();
+            } if (option.equals("--user")) {
+                userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
+            } else if (allowed == null) {
+                allowed = Boolean.valueOf(option);
+            } else {
+                commandHandler.getErrPrintWriter().println("Error: unexpected option: " + option);
+                return false;
+            }
+        }
+
+        if (TextUtils.isEmpty(packageName)) {
+            commandHandler.getErrPrintWriter().println("Error: no package specified");
+            return false;
+        } else if (packageName.equalsIgnoreCase("all")) {
+            packageName = null;
+        }
+
+        if (userId == null) {
+            commandHandler.getErrPrintWriter().println("Error: user ID not specified");
+            return false;
+        }
+
+        if (allowed == null) {
+            commandHandler.getErrPrintWriter().println("Error: allowed setting not specified");
+            return false;
+        }
+
+        userId = translateUserId(userId, "runSetAppLinksAllowed");
+
+        try {
+            mCallback.setDomainVerificationLinkHandlingAllowedInternal(packageName, allowed,
+                    userId);
+        } catch (NameNotFoundException e) {
+            commandHandler.getErrPrintWriter().println("Package not found: " + packageName);
+            return false;
+        }
+
+        return true;
+    }
+
+    private ArrayList<String> getRemainingArgs(@NonNull BasicShellCommandHandler commandHandler) {
+        ArrayList<String> args = new ArrayList<>();
+        String arg;
+        while ((arg = commandHandler.getNextArg()) != null) {
+            args.add(arg);
+        }
+        return args;
+    }
+
+    private int translateUserId(@UserIdInt int userId, @NonNull String logContext) {
+        return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, true, true, logContext, "pm command");
+    }
+
+    /**
+     * Separated interface from {@link DomainVerificationManagerInternal} to hide methods that are
+     * even more internal, and so that testing is easier.
+     */
+    public interface Callback {
+
+        /**
+         * Variant for use by PackageManagerShellCommand to allow the system/developer to override
+         * the state for a domain.
+         *
+         * @param packageName the package whose state to change, or all packages if none is
+         *                    specified
+         * @param state       the new state code, valid values are
+         *                    {@link DomainVerificationState#STATE_NO_RESPONSE},
+         *                    {@link DomainVerificationState#STATE_SUCCESS}, {@link
+         *                    DomainVerificationState#STATE_APPROVED}, and {@link
+         *                    DomainVerificationState#STATE_DENIED}
+         * @param domains     the set of domains to change, or null to change all of them
+         */
+        void setDomainVerificationStatusInternal(@Nullable String packageName, int state,
+                @Nullable ArraySet<String> domains) throws PackageManager.NameNotFoundException;
+
+        /**
+         * Variant for use by PackageManagerShellCommand to allow the system/developer to override
+         * the state for a domain.
+         *
+         * @param packageName the package whose state to change, or all packages if non is
+         *                    specified
+         * @param enabled     whether the domain is now approved by the user
+         * @param domains     the set of domains to change
+         */
+        void setDomainVerificationUserSelectionInternal(@UserIdInt int userId,
+                @Nullable String packageName, boolean enabled, @NonNull ArraySet<String> domains)
+                throws PackageManager.NameNotFoundException;
+
+        /**
+         * @see DomainVerificationManager#getDomainVerificationUserSelection(String)
+         */
+        @Nullable
+        DomainVerificationUserSelection getDomainVerificationUserSelection(
+                @NonNull String packageName, @UserIdInt int userId)
+                throws PackageManager.NameNotFoundException;
+
+        /**
+         * Variant for use by PackageManagerShellCommand to allow the system/developer to override
+         * the setting for a package.
+         *
+         * @param packageName the package whose state to change, or all packages if non is
+         *                    specified
+         * @param allowed     whether the package is allowed to automatically open links through
+         *                    domain verification
+         */
+        void setDomainVerificationLinkHandlingAllowedInternal(@Nullable String packageName,
+                boolean allowed, @UserIdInt int userId) throws NameNotFoundException;
+
+        /**
+         * Reset all the domain verification states for all domains for the given package names, or
+         * all package names if null is provided.
+         */
+        void clearDomainVerificationState(@Nullable List<String> packageNames);
+
+        /**
+         * Reset all the user selections for the given package names, or all package names if null
+         * is provided.
+         */
+        void clearUserSelections(@Nullable List<String> packageNames, @UserIdInt int userId);
+
+        /**
+         * Broadcast a verification request for the given package names, or all package names if
+         * null is provided. By default only re-broadcasts if a package has not recorded a
+         * response.
+         *
+         * @param reVerify send even if the package has previously recorded a response
+         */
+        void verifyPackages(@Nullable List<String> packageNames, boolean reVerify);
+
+        /**
+         * @see DomainVerificationManagerInternal#printState(IndentingPrintWriter, String, Integer)
+         */
+        void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+                @Nullable @UserIdInt Integer userId) throws NameNotFoundException;
+    }
+}
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
new file mode 100644
index 0000000..474f822
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.CheckResult;
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
+
+import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+final class DomainVerificationUtils {
+
+    /**
+     * Consolidates package exception messages. A generic unavailable message is included since the
+     * caller doesn't bother to check why the package isn't available.
+     */
+    @CheckResult
+    static NameNotFoundException throwPackageUnavailable(@NonNull String packageName)
+            throws NameNotFoundException {
+        throw new NameNotFoundException("Package " + packageName + " unavailable");
+    }
+
+    static boolean isDomainVerificationIntent(Intent intent) {
+        return intent.isWebIntent()
+                && intent.hasCategory(Intent.CATEGORY_BROWSABLE)
+                && intent.hasCategory(Intent.CATEGORY_DEFAULT);
+    }
+
+    static boolean isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg,
+            long changeId) {
+        //noinspection ConstantConditions
+        return Binder.withCleanCallingIdentity(
+                () -> platformCompat.isChangeEnabled(changeId, buildMockAppInfo(pkg)));
+    }
+
+    /**
+     * Passed to {@link PlatformCompat} because this can be invoked mid-install process or when
+     * {@link PackageManagerService#mLock} is being held, and {@link PlatformCompat} will not be
+     * able to query the pending {@link ApplicationInfo} from {@link PackageManager}.
+     * <p>
+     * TODO(b/177613575): Can a different API be used?
+     */
+    @NonNull
+    private static ApplicationInfo buildMockAppInfo(@NonNull AndroidPackage pkg) {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = pkg.getPackageName();
+        appInfo.targetSdkVersion = pkg.getTargetSdkVersion();
+        return appInfo;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING b/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING
new file mode 100644
index 0000000..c6c9791
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "PackageManagerServiceUnitTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.pm.test.verify.domain"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
new file mode 100644
index 0000000..48099aa
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.models;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * State for a single package for the domain verification APIs. Stores the state of each individual
+ * domain declared by the package, including its verification state and user selection state.
+ */
+@DataClass(genToString = true, genEqualsHashCode = true)
+public class DomainVerificationPkgState {
+
+    @NonNull
+    private final String mPackageName;
+
+    @NonNull
+    private UUID mId;
+
+    /**
+     * Whether or not the package declares any autoVerify domains. This is separate from an empty
+     * check on the map itself, because an empty map means no response recorded, not necessarily no
+     * domains declared. When this is false, {@link #mStateMap} will be empty, but
+     * {@link #mUserSelectionStates} may contain any domains the user has explicitly chosen to
+     * allow this package to open, which may or may not be marked autoVerify.
+     */
+    private final boolean mHasAutoVerifyDomains;
+
+    /**
+     * Map of domains to state integers. Only domains that are not set to the default value of
+     * {@link DomainVerificationState#STATE_NO_RESPONSE} are included.
+     *
+     * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
+     *  such as storing no state when the package is marked as a linked app in SystemConfig.
+     */
+    @NonNull
+    private final ArrayMap<String, Integer> mStateMap;
+
+    @NonNull
+    private final SparseArray<DomainVerificationUserState> mUserSelectionStates;
+
+    public DomainVerificationPkgState(@NonNull String packageName, @NonNull UUID id,
+            boolean hasAutoVerifyDomains) {
+        this(packageName, id, hasAutoVerifyDomains, new ArrayMap<>(0), new SparseArray<>(0));
+    }
+
+    @Nullable
+    public DomainVerificationUserState getUserSelectionState(@UserIdInt int userId) {
+        return mUserSelectionStates.get(userId);
+    }
+
+    @Nullable
+    public DomainVerificationUserState getOrCreateUserSelectionState(@UserIdInt int userId) {
+        DomainVerificationUserState userState = mUserSelectionStates.get(userId);
+        if (userState == null) {
+            userState = new DomainVerificationUserState(userId);
+            mUserSelectionStates.put(userId, userState);
+        }
+        return userState;
+    }
+
+    public void setId(@NonNull UUID id) {
+        mId = id;
+    }
+
+    public void removeUser(@UserIdInt int userId) {
+        mUserSelectionStates.remove(userId);
+    }
+
+    public void removeAllUsers() {
+        mUserSelectionStates.clear();
+    }
+
+    private int userSelectionStatesHashCode() {
+        return mUserSelectionStates.contentHashCode();
+    }
+
+    private boolean userSelectionStatesEquals(
+            @NonNull SparseArray<DomainVerificationUserState> other) {
+        return mUserSelectionStates.contentEquals(other);
+    }
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new DomainVerificationPkgState.
+     *
+     * @param stateMap
+     *   Map of domains to state integers. Only domains that are not set to the default value of
+     *   {@link DomainVerificationManager#STATE_NO_RESPONSE} are included.
+     *
+     *   TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
+     *    such as storing no state when the package is marked as a linked app in SystemConfig.
+     */
+    @DataClass.Generated.Member
+    public DomainVerificationPkgState(
+            @NonNull String packageName,
+            @NonNull UUID id,
+            boolean hasAutoVerifyDomains,
+            @NonNull ArrayMap<String,Integer> stateMap,
+            @NonNull SparseArray<DomainVerificationUserState> userSelectionStates) {
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mId = id;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mId);
+        this.mHasAutoVerifyDomains = hasAutoVerifyDomains;
+        this.mStateMap = stateMap;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mStateMap);
+        this.mUserSelectionStates = userSelectionStates;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mUserSelectionStates);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull UUID getId() {
+        return mId;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isHasAutoVerifyDomains() {
+        return mHasAutoVerifyDomains;
+    }
+
+    /**
+     * Map of domains to state integers. Only domains that are not set to the default value of
+     * {@link DomainVerificationManager#STATE_NO_RESPONSE} are included.
+     *
+     * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
+     *  such as storing no state when the package is marked as a linked app in SystemConfig.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArrayMap<String,Integer> getStateMap() {
+        return mStateMap;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull SparseArray<DomainVerificationUserState> getUserSelectionStates() {
+        return mUserSelectionStates;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "DomainVerificationPkgState { " +
+                "packageName = " + mPackageName + ", " +
+                "id = " + mId + ", " +
+                "hasAutoVerifyDomains = " + mHasAutoVerifyDomains + ", " +
+                "stateMap = " + mStateMap + ", " +
+                "userSelectionStates = " + mUserSelectionStates +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(DomainVerificationPkgState other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        DomainVerificationPkgState that = (DomainVerificationPkgState) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && Objects.equals(mPackageName, that.mPackageName)
+                && Objects.equals(mId, that.mId)
+                && mHasAutoVerifyDomains == that.mHasAutoVerifyDomains
+                && Objects.equals(mStateMap, that.mStateMap)
+                && userSelectionStatesEquals(that.mUserSelectionStates);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + Objects.hashCode(mPackageName);
+        _hash = 31 * _hash + Objects.hashCode(mId);
+        _hash = 31 * _hash + Boolean.hashCode(mHasAutoVerifyDomains);
+        _hash = 31 * _hash + Objects.hashCode(mStateMap);
+        _hash = 31 * _hash + userSelectionStatesHashCode();
+        return _hash;
+    }
+
+    @DataClass.Generated(
+            time = 1608234185474L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java",
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final  boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.Integer> mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationUserState> mUserSelectionStates\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationUserState getUserSelectionState(int)\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationUserState getOrCreateUserSelectionState(int)\npublic  void setId(java.util.UUID)\npublic  void removeUser(int)\npublic  void removeAllUsers()\nprivate  int userSelectionStatesHashCode()\nprivate  boolean userSelectionStatesEquals(android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationUserState>)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationStateMap.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationStateMap.java
new file mode 100644
index 0000000..88ccd83
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationStateMap.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.models;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * A feature specific implementation of a multi-key map, since lookups by both a {@link String}
+ * package name and {@link UUID} domain set ID should be supported.
+ *
+ * @param <ValueType> stored object type
+ */
+public class DomainVerificationStateMap<ValueType> {
+
+    private static final String TAG = "DomainVerificationStateMap";
+
+    @NonNull
+    private final ArrayMap<String, ValueType> mPackageNameMap = new ArrayMap<>();
+
+    @NonNull
+    private final ArrayMap<UUID, ValueType> mDomainSetIdMap = new ArrayMap<>();
+
+    public int size() {
+        return mPackageNameMap.size();
+    }
+
+    @NonNull
+    public ValueType valueAt(@IntRange(from = 0) int index) {
+        return mPackageNameMap.valueAt(index);
+    }
+
+    @Nullable
+    public ValueType get(@NonNull String packageName) {
+        return mPackageNameMap.get(packageName);
+    }
+
+    @Nullable
+    public ValueType get(@NonNull UUID domainSetId) {
+        return mDomainSetIdMap.get(domainSetId);
+    }
+
+    public void put(@NonNull String packageName, @NonNull UUID domainSetId,
+            @NonNull ValueType valueType) {
+        if (mPackageNameMap.containsKey(packageName)) {
+            remove(packageName);
+        }
+
+        mPackageNameMap.put(packageName, valueType);
+        mDomainSetIdMap.put(domainSetId, valueType);
+    }
+
+    @Nullable
+    public ValueType remove(@NonNull String packageName) {
+        ValueType valueRemoved = mPackageNameMap.remove(packageName);
+        if (valueRemoved != null) {
+            int index = mDomainSetIdMap.indexOfValue(valueRemoved);
+            if (index >= 0) {
+                mDomainSetIdMap.removeAt(index);
+            }
+        }
+        return valueRemoved;
+    }
+
+    @Nullable
+    public ValueType remove(@NonNull UUID domainSetId) {
+        ValueType valueRemoved = mDomainSetIdMap.remove(domainSetId);
+        if (valueRemoved != null) {
+            int index = mPackageNameMap.indexOfValue(valueRemoved);
+            if (index >= 0) {
+                mPackageNameMap.removeAt(index);
+            }
+        }
+        return valueRemoved;
+    }
+
+    @NonNull
+    public List<String> getPackageNames() {
+        return new ArrayList<>(mPackageNameMap.keySet());
+    }
+
+    /**
+     * Exposes the backing values collection of the one of the internal maps. Should only be used
+     * for test assertions.
+     */
+    @VisibleForTesting
+    public Collection<ValueType> values() {
+        return new ArrayList<>(mPackageNameMap.values());
+    }
+
+    @Override
+    public String toString() {
+        return "DomainVerificationStateMap{"
+                + "packageNameMap=" + mPackageNameMap
+                + ", domainSetIdMap=" + mDomainSetIdMap
+                + '}';
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
new file mode 100644
index 0000000..8e82608
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.models;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.util.ArraySet;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Set;
+
+/**
+ * Tracks which domains have been explicitly enabled by the user, allowing it to automatically open
+ * that domain when a web URL Intent is sent ft.
+ */
+@DataClass(genSetters = true, genEqualsHashCode = true, genToString = true)
+public class DomainVerificationUserState {
+
+    @UserIdInt
+    private final int mUserId;
+
+    /** List of domains which have been enabled by the user. **/
+    @NonNull
+    private final ArraySet<String> mEnabledHosts;
+
+    /** Whether to disallow this package from automatically opening links by auto verification. */
+    private boolean mDisallowLinkHandling;
+
+    public DomainVerificationUserState(@UserIdInt int userId) {
+        mUserId = userId;
+        mEnabledHosts = new ArraySet<>();
+    }
+
+    public DomainVerificationUserState addHosts(@NonNull ArraySet<String> newHosts) {
+        mEnabledHosts.addAll(newHosts);
+        return this;
+    }
+
+    public DomainVerificationUserState addHosts(@NonNull Set<String> newHosts) {
+        mEnabledHosts.addAll(newHosts);
+        return this;
+    }
+
+    public DomainVerificationUserState removeHosts(@NonNull ArraySet<String> newHosts) {
+        mEnabledHosts.removeAll(newHosts);
+        return this;
+    }
+
+    public DomainVerificationUserState removeHosts(@NonNull Set<String> newHosts) {
+        mEnabledHosts.removeAll(newHosts);
+        return this;
+    }
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new DomainVerificationUserState.
+     *
+     * @param enabledHosts
+     *   List of domains which have been enabled by the user. *
+     */
+    @DataClass.Generated.Member
+    public DomainVerificationUserState(
+            @UserIdInt int userId,
+            @NonNull ArraySet<String> enabledHosts,
+            boolean disallowLinkHandling) {
+        this.mUserId = userId;
+        com.android.internal.util.AnnotationValidations.validate(
+                UserIdInt.class, null, mUserId);
+        this.mEnabledHosts = enabledHosts;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mEnabledHosts);
+        this.mDisallowLinkHandling = disallowLinkHandling;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @UserIdInt int getUserId() {
+        return mUserId;
+    }
+
+    /**
+     * List of domains which have been enabled by the user. *
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArraySet<String> getEnabledHosts() {
+        return mEnabledHosts;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isDisallowLinkHandling() {
+        return mDisallowLinkHandling;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull DomainVerificationUserState setDisallowLinkHandling( boolean value) {
+        mDisallowLinkHandling = value;
+        return this;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "DomainVerificationUserState { " +
+                "userId = " + mUserId + ", " +
+                "enabledHosts = " + mEnabledHosts + ", " +
+                "disallowLinkHandling = " + mDisallowLinkHandling +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(DomainVerificationUserState other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        DomainVerificationUserState that = (DomainVerificationUserState) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mUserId == that.mUserId
+                && java.util.Objects.equals(mEnabledHosts, that.mEnabledHosts)
+                && mDisallowLinkHandling == that.mDisallowLinkHandling;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + mUserId;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mEnabledHosts);
+        _hash = 31 * _hash + Boolean.hashCode(mDisallowLinkHandling);
+        return _hash;
+    }
+
+    @DataClass.Generated(
+            time = 1608234273324L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java",
+            inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledHosts\nprivate  boolean mDisallowLinkHandling\npublic  com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(java.util.Set<java.lang.String>)\nclass DomainVerificationUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
new file mode 100644
index 0000000..715d8fb
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.proxy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.server.DeviceIdleInternal;
+import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
+import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+
+import java.util.Objects;
+import java.util.Set;
+
+// TODO(b/170321181): Combine the proxy versions for supporting v1 and v2 at once
+public interface DomainVerificationProxy {
+
+    String TAG = "DomainVerificationProxy";
+
+    boolean DEBUG_PROXIES = false;
+
+    static <ConnectionType extends DomainVerificationProxyV1.Connection
+            & DomainVerificationProxyV2.Connection> DomainVerificationProxy makeProxy(
+            @Nullable ComponentName componentV1, @Nullable ComponentName componentV2,
+            @NonNull Context context, @NonNull DomainVerificationManagerInternal manager,
+            @NonNull DomainVerificationCollector collector, @NonNull ConnectionType connection) {
+        if (DEBUG_PROXIES) {
+            Slog.d(TAG, "Intent filter verification agent: " + componentV1);
+            Slog.d(TAG, "Domain verification agent: " + componentV2);
+        }
+
+        if (componentV2 != null && componentV1 != null
+                && !Objects.equals(componentV2.getPackageName(), componentV1.getPackageName())) {
+            // Only allow a legacy verifier if it's in the same package as the v2 verifier
+            componentV1 = null;
+        }
+
+        DomainVerificationProxy proxyV1 = null;
+        DomainVerificationProxy proxyV2 = null;
+
+        if (componentV1 != null) {
+            proxyV1 = new DomainVerificationProxyV1(context, manager, collector, connection,
+                    componentV1);
+        }
+
+        if (componentV2 != null) {
+            proxyV2 = new DomainVerificationProxyV2(context, connection, componentV2);
+        }
+
+        if (proxyV1 != null && proxyV2 != null) {
+            return new DomainVerificationProxyCombined(proxyV1, proxyV2);
+        }
+
+        if (proxyV1 != null) {
+            return proxyV1;
+        }
+
+        if (proxyV2 != null) {
+            return proxyV2;
+        }
+
+        return new DomainVerificationProxyUnavailable();
+    }
+
+    default void sendBroadcastForPackages(@NonNull Set<String> packageNames) {
+    }
+
+    /**
+     * Runs a message on the caller's Handler as a result of {@link BaseConnection#schedule(int,
+     * Object)}. Abstracts the actual scheduling/running from the manager class. This is also
+     * necessary so that different what codes can be used depending on the verifier proxy on device,
+     * to allow backporting v1. The backport proxy may schedule more or less messages than the v2
+     * proxy.
+     *
+     * @param messageCode One of the values in {@link DomainVerificationMessageCodes}.
+     * @param object      Arbitrary object that was originally included.
+     */
+    default boolean runMessage(int messageCode, Object object) {
+        return false;
+    }
+
+    default boolean isCallerVerifier(int callingUid) {
+        return false;
+    }
+
+    @Nullable
+    default ComponentName getComponentName() {
+        return null;
+    }
+
+    interface BaseConnection {
+
+        /**
+         * Schedule something to be run later. The implementation is left up to the caller.
+         *
+         * @param code   One of the values in {@link DomainVerificationMessageCodes}.
+         * @param object Arbitrary object to include with the message.
+         */
+        void schedule(int code, @Nullable Object object);
+
+        long getPowerSaveTempWhitelistAppDuration();
+
+        DeviceIdleInternal getDeviceIdleInternal();
+
+        boolean isCallerPackage(int callingUid, @NonNull String packageName);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyCombined.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyCombined.java
new file mode 100644
index 0000000..8571c08
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyCombined.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.proxy;
+
+import android.annotation.NonNull;
+
+import java.util.Set;
+
+class DomainVerificationProxyCombined implements DomainVerificationProxy {
+
+    @NonNull
+    private final DomainVerificationProxy mProxyV1;
+    @NonNull
+    private final DomainVerificationProxy mProxyV2;
+
+    DomainVerificationProxyCombined(@NonNull DomainVerificationProxy proxyV1,
+            @NonNull DomainVerificationProxy proxyV2) {
+        mProxyV1 = proxyV1;
+        mProxyV2 = proxyV2;
+    }
+
+    @Override
+    public void sendBroadcastForPackages(@NonNull Set<String> packageNames) {
+        mProxyV2.sendBroadcastForPackages(packageNames);
+        mProxyV1.sendBroadcastForPackages(packageNames);
+    }
+
+    @Override
+    public boolean runMessage(int messageCode, Object object) {
+        // Both proxies must run, so cannot use a direct ||, which may skip the right hand side
+        boolean resultV2 = mProxyV2.runMessage(messageCode, object);
+        boolean resultV1 = mProxyV1.runMessage(messageCode, object);
+        return resultV2 || resultV1;
+    }
+
+    @Override
+    public boolean isCallerVerifier(int callingUid) {
+        return mProxyV2.isCallerVerifier(callingUid) || mProxyV1.isCallerVerifier(callingUid);
+    }
+}
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java
similarity index 73%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java
index 19b20f2..bd77983 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-package android.graphics.fonts;
+package com.android.server.pm.verify.domain.proxy;
 
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/** Stub implementation for when the verification agent is unavailable */
+public class DomainVerificationProxyUnavailable implements DomainVerificationProxy {
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
new file mode 100644
index 0000000..eab89e98
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.proxy;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.BroadcastOptions;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+public class DomainVerificationProxyV1 implements DomainVerificationProxy {
+
+    private static final String TAG = "DomainVerificationProxyV1";
+
+    private static final boolean DEBUG_BROADCASTS = false;
+
+    @NonNull
+    private final Context mContext;
+
+    @NonNull
+    private final Connection mConnection;
+
+    @NonNull
+    private final ComponentName mVerifierComponent;
+
+    @NonNull
+    private final DomainVerificationManagerInternal mManager;
+
+    @NonNull
+    private final DomainVerificationCollector mCollector;
+
+    @NonNull
+    private final Object mLock = new Object();
+
+    @NonNull
+    @GuardedBy("mLock")
+    private final ArrayMap<Integer, Pair<UUID, String>> mRequests = new ArrayMap<>();
+
+    @GuardedBy("mLock")
+    private int mVerificationToken = 0;
+
+    public DomainVerificationProxyV1(@NonNull Context context,
+            @NonNull DomainVerificationManagerInternal manager,
+            @NonNull DomainVerificationCollector collector, @NonNull Connection connection,
+            @NonNull ComponentName verifierComponent) {
+        mContext = context;
+        mConnection = connection;
+        mVerifierComponent = verifierComponent;
+        mManager = manager;
+        mCollector = collector;
+    }
+
+    public static void queueLegacyVerifyResult(@NonNull Context context,
+            @NonNull DomainVerificationProxyV1.Connection connection, int verificationId,
+            int verificationCode, @Nullable List<String> failedDomains, int callingUid) {
+        context.enforceCallingOrSelfPermission(
+                Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+                "Only the intent filter verification agent can verify applications");
+
+        connection.schedule(DomainVerificationMessageCodes.LEGACY_ON_INTENT_FILTER_VERIFIED,
+                new Response(callingUid, verificationId, verificationCode, failedDomains));
+    }
+
+    @Override
+    public void sendBroadcastForPackages(@NonNull Set<String> packageNames) {
+        synchronized (mLock) {
+            int size = mRequests.size();
+            for (int index = size - 1; index >= 0; index--) {
+                Pair<UUID, String> pair = mRequests.valueAt(index);
+                if (packageNames.contains(pair.second)) {
+                    mRequests.removeAt(index);
+                }
+            }
+        }
+        mConnection.schedule(DomainVerificationMessageCodes.LEGACY_SEND_REQUEST, packageNames);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public boolean runMessage(int messageCode, Object object) {
+        switch (messageCode) {
+            case DomainVerificationMessageCodes.LEGACY_SEND_REQUEST:
+                @SuppressWarnings("unchecked") Set<String> packageNames = (Set<String>) object;
+                if (DEBUG_BROADCASTS) {
+                    Slog.d(TAG, "Requesting domain verification for " + packageNames);
+                }
+
+                ArrayMap<Integer, Pair<UUID, String>> newRequests = new ArrayMap<>(
+                        packageNames.size());
+                synchronized (mLock) {
+                    for (String packageName : packageNames) {
+                        UUID domainSetId = mManager.getDomainVerificationInfoId(packageName);
+                        if (domainSetId == null) {
+                            continue;
+                        }
+
+                        newRequests.put(mVerificationToken++,
+                                Pair.create(domainSetId, packageName));
+                    }
+                    mRequests.putAll(newRequests);
+                }
+
+                sendBroadcasts(newRequests);
+                return true;
+            case DomainVerificationMessageCodes.LEGACY_ON_INTENT_FILTER_VERIFIED:
+                Response response = (Response) object;
+
+                Pair<UUID, String> pair = mRequests.get(response.verificationId);
+                if (pair == null) {
+                    return true;
+                }
+
+                UUID domainSetId = pair.first;
+                String packageName = pair.second;
+                DomainVerificationInfo set;
+                try {
+                    set = mManager.getDomainVerificationInfo(packageName);
+                } catch (PackageManager.NameNotFoundException ignored) {
+                    return true;
+                }
+
+                if (!Objects.equals(domainSetId, set.getIdentifier())) {
+                    return true;
+                }
+
+                Set<String> successfulDomains = new ArraySet<>(set.getHostToStateMap().keySet());
+                successfulDomains.removeAll(response.failedDomains);
+
+                int callingUid = response.callingUid;
+                try {
+                    mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+                            successfulDomains, DomainVerificationState.STATE_SUCCESS);
+                } catch (DomainVerificationManager.InvalidDomainSetException
+                        | PackageManager.NameNotFoundException ignored) {
+                }
+                try {
+                    mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+                            new ArraySet<>(response.failedDomains),
+                            DomainVerificationState.STATE_LEGACY_FAILURE);
+                } catch (DomainVerificationManager.InvalidDomainSetException
+                        | PackageManager.NameNotFoundException ignored) {
+                }
+
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public boolean isCallerVerifier(int callingUid) {
+        return mConnection.isCallerPackage(callingUid, mVerifierComponent.getPackageName());
+    }
+
+    @SuppressWarnings("deprecation")
+    private void sendBroadcasts(@NonNull ArrayMap<Integer, Pair<UUID, String>> verifications) {
+        final long allowListTimeout = mConnection.getPowerSaveTempWhitelistAppDuration();
+        mConnection.getDeviceIdleInternal().addPowerSaveTempWhitelistApp(Process.myUid(),
+                mVerifierComponent.getPackageName(), allowListTimeout,
+                UserHandle.USER_SYSTEM, true, "domain verification agent");
+
+        int size = verifications.size();
+        for (int index = 0; index < size; index++) {
+            int verificationId = verifications.keyAt(index);
+            String packageName = verifications.valueAt(index).second;
+            AndroidPackage pkg = mConnection.getPackage(packageName);
+
+            String hostsString = buildHostsString(pkg);
+
+            Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION)
+                    .setComponent(mVerifierComponent)
+                    .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
+                            verificationId)
+                    .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
+                            IntentFilter.SCHEME_HTTPS)
+                    .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
+                            hostsString)
+                    .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
+                            packageName)
+                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+            final BroadcastOptions options = BroadcastOptions.makeBasic();
+            options.setTemporaryAppWhitelistDuration(allowListTimeout);
+            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null, options.toBundle());
+        }
+    }
+
+    @NonNull
+    private String buildHostsString(@NonNull AndroidPackage pkg) {
+        // The collector itself handles the v1 vs v2 behavior, which is based on targetSdkVersion,
+        // not the version of the verification agent on device.
+        ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
+        return TextUtils.join(" ", domains);
+    }
+
+    private static class Response {
+        public final int callingUid;
+        public final int verificationId;
+        public final int verificationCode;
+        @NonNull
+        public final List<String> failedDomains;
+
+        private Response(int callingUid, int verificationId, int verificationCode,
+                @Nullable List<String> failedDomains) {
+            this.callingUid = callingUid;
+            this.verificationId = verificationId;
+            this.verificationCode = verificationCode;
+            this.failedDomains = failedDomains == null ? Collections.emptyList() : failedDomains;
+        }
+    }
+
+    public interface Connection extends BaseConnection {
+
+        @Nullable
+        AndroidPackage getPackage(@NonNull String packageName);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
new file mode 100644
index 0000000..9fcbce2
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.proxy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.BroadcastOptions;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationRequest;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
+
+import java.util.Set;
+
+public class DomainVerificationProxyV2 implements DomainVerificationProxy {
+
+    private static final String TAG = "DomainVerificationProxyV2";
+
+    private static final boolean DEBUG_BROADCASTS = true;
+
+    @NonNull
+    private final Context mContext;
+
+    @NonNull
+    private final Connection mConnection;
+
+    @NonNull
+    private final ComponentName mVerifierComponent;
+
+    public DomainVerificationProxyV2(@NonNull Context context, @NonNull Connection connection,
+            @NonNull ComponentName verifierComponent) {
+        mContext = context;
+        mConnection = connection;
+        mVerifierComponent = verifierComponent;
+    }
+
+    @Override
+    public void sendBroadcastForPackages(@NonNull Set<String> packageNames) {
+        mConnection.schedule(com.android.server.pm.verify.domain.DomainVerificationMessageCodes.SEND_REQUEST, packageNames);
+    }
+
+    @Override
+    public boolean runMessage(int messageCode, Object object) {
+        switch (messageCode) {
+            case DomainVerificationMessageCodes.SEND_REQUEST:
+                @SuppressWarnings("unchecked") Set<String> packageNames = (Set<String>) object;
+                DomainVerificationRequest request = new DomainVerificationRequest(packageNames);
+
+                final long allowListTimeout = mConnection.getPowerSaveTempWhitelistAppDuration();
+                final BroadcastOptions options = BroadcastOptions.makeBasic();
+                options.setTemporaryAppWhitelistDuration(allowListTimeout);
+
+                mConnection.getDeviceIdleInternal().addPowerSaveTempWhitelistApp(Process.myUid(),
+                        mVerifierComponent.getPackageName(), allowListTimeout,
+                        UserHandle.USER_SYSTEM, true, "domain verification agent");
+
+                Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION)
+                        .setComponent(mVerifierComponent)
+                        .putExtra(DomainVerificationManager.EXTRA_VERIFICATION_REQUEST, request)
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+                if (DEBUG_BROADCASTS) {
+                    Slog.d(TAG, "Requesting domain verification for " + packageNames);
+                }
+
+                mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null, options.toBundle());
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public boolean isCallerVerifier(int callingUid) {
+        return mConnection.isCallerPackage(callingUid, mVerifierComponent.getPackageName());
+    }
+
+    @Nullable
+    @Override
+    public ComponentName getComponentName() {
+        return mVerifierComponent;
+    }
+
+    public interface Connection extends BaseConnection {
+    }
+}
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index e901f66f..ac358db 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -34,10 +34,11 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
+import com.android.server.devicestate.DeviceState;
 import com.android.server.devicestate.DeviceStateProvider;
 import com.android.server.policy.devicestate.config.Conditions;
-import com.android.server.policy.devicestate.config.DeviceState;
 import com.android.server.policy.devicestate.config.DeviceStateConfig;
 import com.android.server.policy.devicestate.config.LidSwitchCondition;
 import com.android.server.policy.devicestate.config.NumericRange;
@@ -54,6 +55,7 @@
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.function.BooleanSupplier;
@@ -82,7 +84,7 @@
     private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true;
 
     @VisibleForTesting
-    static final int DEFAULT_DEVICE_STATE = 0;
+    static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT");
 
     private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
     private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
@@ -116,31 +118,38 @@
     @VisibleForTesting
     static DeviceStateProviderImpl createFromConfig(@NonNull Context context,
             @Nullable ReadableConfig readableConfig) {
-        SparseArray<Conditions> conditionsForState = new SparseArray<>();
+        List<DeviceState> deviceStateList = new ArrayList<>();
+        List<Conditions> conditionsList = new ArrayList<>();
+
         if (readableConfig != null) {
             DeviceStateConfig config = parseConfig(readableConfig);
             if (config != null) {
-                for (DeviceState stateConfig : config.getDeviceState()) {
-                    int state = stateConfig.getIdentifier().intValue();
-                    Conditions conditions = stateConfig.getConditions();
-                    conditionsForState.put(state, conditions);
+                for (com.android.server.policy.devicestate.config.DeviceState stateConfig :
+                        config.getDeviceState()) {
+                    final int state = stateConfig.getIdentifier().intValue();
+                    final String name = stateConfig.getName() == null ? "" : stateConfig.getName();
+                    deviceStateList.add(new DeviceState(state, name));
+
+                    final Conditions condition = stateConfig.getConditions();
+                    conditionsList.add(condition);
                 }
             }
         }
 
-        if (conditionsForState.size() == 0) {
-            conditionsForState.put(DEFAULT_DEVICE_STATE, null);
+        if (deviceStateList.size() == 0) {
+            deviceStateList.add(DEFAULT_DEVICE_STATE);
+            conditionsList.add(null);
         }
-        return new DeviceStateProviderImpl(context, conditionsForState);
+        return new DeviceStateProviderImpl(context, deviceStateList, conditionsList);
     }
 
     // Lock for internal state.
     private final Object mLock = new Object();
     private final Context mContext;
-    // List of supported states in ascending order.
-    private final int[] mOrderedStates;
-    // Map of state to a boolean supplier that returns true when all required conditions are met for
-    // the device to be in the state.
+    // List of supported states in ascending order based on their identifier.
+    private final DeviceState[] mOrderedStates;
+    // Map of state identifier to a boolean supplier that returns true when all required conditions
+    // are met for the device to be in the state.
     private final SparseArray<BooleanSupplier> mStateConditions;
 
     @Nullable
@@ -155,12 +164,16 @@
     private final Map<Sensor, SensorEvent> mLatestSensorEvent = new ArrayMap<>();
 
     private DeviceStateProviderImpl(@NonNull Context context,
-            @NonNull SparseArray<Conditions> conditionsForState) {
+            @NonNull List<DeviceState> deviceStates,
+            @NonNull List<Conditions> stateConditions) {
+        Preconditions.checkArgument(deviceStates.size() == stateConditions.size(),
+                "Number of device states must be equal to the number of device state conditions.");
+
         mContext = context;
-        mOrderedStates = new int[conditionsForState.size()];
-        for (int i = 0; i < conditionsForState.size(); i++) {
-            mOrderedStates[i] = conditionsForState.keyAt(i);
-        }
+
+        DeviceState[] orderedStates = deviceStates.toArray(new DeviceState[deviceStates.size()]);
+        Arrays.sort(orderedStates, Comparator.comparingInt(DeviceState::getIdentifier));
+        mOrderedStates = orderedStates;
 
         // Whether or not this instance should register to receive lid switch notifications from
         // InputManagerInternal. If there are no device state conditions that are based on the lid
@@ -171,9 +184,9 @@
         final ArraySet<Sensor> sensorsToListenTo = new ArraySet<>();
 
         mStateConditions = new SparseArray<>();
-        for (int i = 0; i < mOrderedStates.length; i++) {
-            int state = mOrderedStates[i];
-            Conditions conditions = conditionsForState.get(state);
+        for (int i = 0; i < stateConditions.size(); i++) {
+            final int state = deviceStates.get(i).getIdentifier();
+            final Conditions conditions = stateConditions.get(i);
             if (conditions == null) {
                 mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
                 continue;
@@ -261,7 +274,7 @@
 
     /** Notifies the listener that the set of supported device states has changed. */
     private void notifySupportedStatesChanged() {
-        int[] supportedStates;
+        DeviceState[] supportedStates;
         synchronized (mLock) {
             if (mListener == null) {
                 return;
@@ -281,9 +294,9 @@
                 return;
             }
 
-            int newState = mOrderedStates[0];
+            int newState = mOrderedStates[0].getIdentifier();
             for (int i = 0; i < mOrderedStates.length; i++) {
-                int state = mOrderedStates[i];
+                int state = mOrderedStates[i].getIdentifier();
                 if (mStateConditions.get(state).getAsBoolean()) {
                     newState = state;
                     break;
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index ff51237..c10e828 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -209,16 +209,23 @@
      * resource.
      */
     private class DeviceStateListener implements DeviceStateManager.DeviceStateListener {
-        private final int mFoldedDeviceState;
+        private final int[] mFoldedDeviceStates;
 
         DeviceStateListener(Context context) {
-            mFoldedDeviceState = context.getResources().getInteger(
-                    com.android.internal.R.integer.config_foldedDeviceState);
+            mFoldedDeviceStates = context.getResources().getIntArray(
+                    com.android.internal.R.array.config_foldedDeviceStates);
         }
 
         @Override
         public void onDeviceStateChanged(int deviceState) {
-            setDeviceFolded(deviceState == mFoldedDeviceState);
+            boolean folded = false;
+            for (int i = 0; i < mFoldedDeviceStates.length; i++) {
+                if (deviceState == mFoldedDeviceStates[i]) {
+                    folded = true;
+                    break;
+                }
+            }
+            setDeviceFolded(folded);
         }
     }
 }
diff --git a/services/core/java/com/android/server/policy/IconUtilities.java b/services/core/java/com/android/server/policy/IconUtilities.java
deleted file mode 100644
index 884d7d4..0000000
--- a/services/core/java/com/android/server/policy/IconUtilities.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2008 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.policy;
-
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.PaintDrawable;
-import android.graphics.drawable.StateListDrawable;
-import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
-import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.TableMaskFilter;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.content.res.Resources;
-import android.content.Context;
-
-/**
- * Various utilities shared amongst the Launcher's classes.
- */
-public final class IconUtilities {
-
-    private int mIconWidth = -1;
-    private int mIconHeight = -1;
-    private int mIconTextureWidth = -1;
-    private int mIconTextureHeight = -1;
-
-    private final Rect mOldBounds = new Rect();
-    private final Canvas mCanvas = new Canvas();
-    private final DisplayMetrics mDisplayMetrics;
-
-    private ColorFilter mDisabledColorFilter;
-
-    public IconUtilities(Context context) {
-        final Resources resources = context.getResources();
-        DisplayMetrics metrics = mDisplayMetrics = resources.getDisplayMetrics();
-        final float density = metrics.density;
-        final float blurPx = 5 * density;
-
-        mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
-        mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2);
-        mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
-                Paint.FILTER_BITMAP_FLAG));
-    }
-
-    /**
-     * Returns a bitmap suitable for the all apps view.  The bitmap will be a power
-     * of two sized ARGB_8888 bitmap that can be used as a gl texture.
-     */
-    public Bitmap createIconBitmap(Drawable icon) {
-        int width = mIconWidth;
-        int height = mIconHeight;
-
-        if (icon instanceof PaintDrawable) {
-            PaintDrawable painter = (PaintDrawable) icon;
-            painter.setIntrinsicWidth(width);
-            painter.setIntrinsicHeight(height);
-        } else if (icon instanceof BitmapDrawable) {
-            // Ensure the bitmap has a density.
-            BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
-            Bitmap bitmap = bitmapDrawable.getBitmap();
-            if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
-                bitmapDrawable.setTargetDensity(mDisplayMetrics);
-            }
-        }
-        int sourceWidth = icon.getIntrinsicWidth();
-        int sourceHeight = icon.getIntrinsicHeight();
-
-        if (sourceWidth > 0 && sourceHeight > 0) {
-            // There are intrinsic sizes.
-            if (width < sourceWidth || height < sourceHeight) {
-                // It's too big, scale it down.
-                final float ratio = (float) sourceWidth / sourceHeight;
-                if (sourceWidth > sourceHeight) {
-                    height = (int) (width / ratio);
-                } else if (sourceHeight > sourceWidth) {
-                    width = (int) (height * ratio);
-                }
-            } else if (sourceWidth < width && sourceHeight < height) {
-                // It's small, use the size they gave us.
-                width = sourceWidth;
-                height = sourceHeight;
-            }
-        }
-
-        // no intrinsic size --> use default size
-        int textureWidth = mIconTextureWidth;
-        int textureHeight = mIconTextureHeight;
-
-        final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
-                Bitmap.Config.ARGB_8888);
-        final Canvas canvas = mCanvas;
-        canvas.setBitmap(bitmap);
-
-        final int left = (textureWidth-width) / 2;
-        final int top = (textureHeight-height) / 2;
-
-        mOldBounds.set(icon.getBounds());
-        icon.setBounds(left, top, left+width, top+height);
-        icon.draw(canvas);
-        icon.setBounds(mOldBounds);
-
-        return bitmap;
-    }
-
-    public ColorFilter getDisabledColorFilter() {
-        if (mDisabledColorFilter != null) {
-            return mDisabledColorFilter;
-        }
-        ColorMatrix brightnessMatrix = new ColorMatrix();
-        float brightnessF = 0.5f;
-        int brightnessI = (int) (255 * brightnessF);
-        // Brightness: C-new = C-old*(1-amount) + amount
-        float scale = 1f - brightnessF;
-        float[] mat = brightnessMatrix.getArray();
-        mat[0] = scale;
-        mat[6] = scale;
-        mat[12] = scale;
-        mat[4] = brightnessI;
-        mat[9] = brightnessI;
-        mat[14] = brightnessI;
-
-        ColorMatrix filterMatrix = new ColorMatrix();
-        filterMatrix.setSaturation(0);
-        filterMatrix.preConcat(brightnessMatrix);
-
-        mDisabledColorFilter = new ColorMatrixColorFilter(filterMatrix);
-        return mDisabledColorFilter;
-    }
-}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a407e8e..bc81961 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -222,6 +222,7 @@
 import com.android.server.wm.DisplayRotation;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import com.android.server.wm.WindowManagerService;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -1913,6 +1914,7 @@
                 handleStartTransitionForKeyguardLw(keyguardGoingAway, 0 /* duration */);
             }
         });
+
         mKeyguardDelegate = new KeyguardServiceDelegate(mContext,
                 new StateCallback() {
                     @Override
@@ -3136,7 +3138,7 @@
     private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) {
         final int res = applyKeyguardOcclusionChange();
         if (res != 0) return res;
-        if (keyguardGoingAway) {
+        if (!WindowManagerService.sEnableRemoteKeyguardAnimation && keyguardGoingAway) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
             startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
         }
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e9d6440..c77e266 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1158,7 +1158,7 @@
      * @param startTime the start time of the animation in uptime milliseconds
      * @param fadeoutDuration the duration of the exit animation, in milliseconds
      */
-    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+    void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
 
     /**
      * Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index c2a1c79..a95628f 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -29,6 +29,7 @@
 import com.android.internal.policy.IKeyguardService;
 import com.android.server.UiThread;
 import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
+import com.android.server.wm.WindowManagerService;
 
 import java.io.PrintWriter;
 
@@ -398,7 +399,7 @@
     }
 
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
-        if (mKeyguardService != null) {
+        if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
             mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
         }
     }
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
index eb9df75..9a91848 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
@@ -120,7 +120,7 @@
          * @return List of EnergyMeasurement objects containing energy measurements for all
          *         available energy meters.
          */
-        android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds);
+        android.hardware.power.stats.EnergyMeasurement[] readEnergyMeter(int[] channelIds);
 
         /**
          * Returns boolean indicating if connection to power stats HAL was established.
@@ -235,13 +235,13 @@
         }
 
         @Override
-        public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds) {
+        public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeter(int[] channelIds) {
             android.hardware.power.stats.EnergyMeasurement[] energyMeasurementHAL = null;
 
             if (sVintfPowerStats != null) {
                 try {
                     energyMeasurementHAL =
-                        sVintfPowerStats.get().readEnergyMeters(channelIds);
+                        sVintfPowerStats.get().readEnergyMeter(channelIds);
                 } catch (RemoteException e) {
                     if (DEBUG) Slog.d(TAG, "Failed to get energy measurements from PowerStats HAL");
                 }
@@ -311,7 +311,7 @@
         }
 
         @Override
-        public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds) {
+        public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeter(int[] channelIds) {
             return nativeReadEnergyMeters(channelIds);
         }
 
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index 78a227e..37fc5a0 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -53,8 +53,9 @@
 public final class PowerStatsLogger extends Handler {
     private static final String TAG = PowerStatsLogger.class.getSimpleName();
     private static final boolean DEBUG = false;
-    protected static final int MSG_LOG_TO_DATA_STORAGE_TIMER = 0;
-    protected static final int MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP = 1;
+    protected static final int MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP = 0;
+    protected static final int MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY = 1;
+    protected static final int MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY = 2;
 
     private final PowerStatsDataStorage mPowerStatsMeterStorage;
     private final PowerStatsDataStorage mPowerStatsModelStorage;
@@ -64,22 +65,33 @@
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
-            case MSG_LOG_TO_DATA_STORAGE_TIMER:
-                if (DEBUG) Slog.d(TAG, "Logging to data storage on timer");
+            case MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY:
+                if (DEBUG) Slog.d(TAG, "Logging to data storage on high frequency timer");
 
                 // Log power meter data.
                 EnergyMeasurement[] energyMeasurements =
-                    mPowerStatsHALWrapper.readEnergyMeters(new int[0]);
+                    mPowerStatsHALWrapper.readEnergyMeter(new int[0]);
                 mPowerStatsMeterStorage.write(
                         EnergyMeasurementUtils.getProtoBytes(energyMeasurements));
                 if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements);
 
-                // Log power model data.
-                EnergyConsumerResult[] energyConsumerResults =
+                // Log power model data without attribution data.
+                EnergyConsumerResult[] ecrNoAttribution =
                     mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
                 mPowerStatsModelStorage.write(
-                        EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults));
-                if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults);
+                        EnergyConsumerResultUtils.getProtoBytes(ecrNoAttribution, false));
+                if (DEBUG) EnergyConsumerResultUtils.print(ecrNoAttribution);
+                break;
+
+            case MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY:
+                if (DEBUG) Slog.d(TAG, "Logging to data storage on low frequency timer");
+
+                // Log power model data with attribution data.
+                EnergyConsumerResult[] ecrAttribution =
+                    mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
+                mPowerStatsModelStorage.write(
+                        EnergyConsumerResultUtils.getProtoBytes(ecrAttribution, true));
+                if (DEBUG) EnergyConsumerResultUtils.print(ecrAttribution);
                 break;
 
             case MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP:
@@ -163,7 +175,7 @@
                         // deserialize, then re-serialize.  This is computationally inefficient.
                         EnergyConsumerResult[] energyConsumerResult =
                             EnergyConsumerResultUtils.unpackProtoMessage(data);
-                        EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos);
+                        EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos, true);
                         if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResult);
                     } catch (IOException e) {
                         Slog.e(TAG, "Failed to write energy model data to incident report.");
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 5fe5db6..ea41980 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -21,7 +21,9 @@
 import android.hardware.power.stats.Channel;
 import android.hardware.power.stats.EnergyConsumer;
 import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
 import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.StateResidencyResult;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -245,10 +247,50 @@
                             future, energyConsumerIds));
             return future;
         }
+
+        @Override
+        public PowerEntity[] getPowerEntityInfo() {
+            return getPowerStatsHal().getPowerEntityInfo();
+        }
+
+        @Override
+        public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
+                int[] powerEntityIds) {
+            final CompletableFuture<StateResidencyResult[]> future = new CompletableFuture<>();
+            mHandler.sendMessage(
+                    PooledLambda.obtainMessage(PowerStatsService.this::getStateResidencyAsync,
+                            future, powerEntityIds));
+            return future;
+        }
+
+        @Override
+        public Channel[] getEnergyMeterInfo() {
+            return getPowerStatsHal().getEnergyMeterInfo();
+        }
+
+        @Override
+        public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
+                int[] channelIds) {
+            final CompletableFuture<EnergyMeasurement[]> future = new CompletableFuture<>();
+            mHandler.sendMessage(
+                    PooledLambda.obtainMessage(PowerStatsService.this::readEnergyMeterAsync,
+                            future, channelIds));
+            return future;
+        }
     }
 
     private void getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future,
             int[] energyConsumerIds) {
         future.complete(getPowerStatsHal().getEnergyConsumed(energyConsumerIds));
     }
+
+    private void getStateResidencyAsync(CompletableFuture<StateResidencyResult[]> future,
+            int[] powerEntityIds) {
+        future.complete(getPowerStatsHal().getStateResidency(powerEntityIds));
+    }
+
+    private void readEnergyMeterAsync(CompletableFuture<EnergyMeasurement[]> future,
+            int[] channelIds) {
+        future.complete(getPowerStatsHal().readEnergyMeter(channelIds));
+    }
 }
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
index 766cf9c..bd003d3 100644
--- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -18,6 +18,7 @@
 
 import android.hardware.power.stats.Channel;
 import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerAttribution;
 import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyMeasurement;
 import android.hardware.power.stats.PowerEntity;
@@ -433,23 +434,40 @@
     }
 
     static class EnergyConsumerResultUtils {
-        public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult) {
+        public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult,
+                boolean includeAttribution) {
             ProtoOutputStream pos = new ProtoOutputStream();
-            packProtoMessage(energyConsumerResult, pos);
+            packProtoMessage(energyConsumerResult, pos, includeAttribution);
             return pos.getBytes();
         }
 
         public static void packProtoMessage(EnergyConsumerResult[] energyConsumerResult,
-                ProtoOutputStream pos) {
+                ProtoOutputStream pos, boolean includeAttribution) {
             if (energyConsumerResult == null) return;
 
             for (int i = 0; i < energyConsumerResult.length; i++) {
-                long token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+                long ecrToken = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
                 pos.write(EnergyConsumerResultProto.ID, energyConsumerResult[i].id);
                 pos.write(EnergyConsumerResultProto.TIMESTAMP_MS,
                         energyConsumerResult[i].timestampMs);
                 pos.write(EnergyConsumerResultProto.ENERGY_UWS, energyConsumerResult[i].energyUWs);
-                pos.end(token);
+
+                if (includeAttribution) {
+                    final int attributionLength = energyConsumerResult[i].attribution.length;
+
+                    for (int j = 0; j < attributionLength; j++) {
+                        final EnergyConsumerAttribution energyConsumerAttribution =
+                                energyConsumerResult[i].attribution[j];
+                        final long ecaToken = pos.start(EnergyConsumerResultProto.ATTRIBUTION);
+                        pos.write(EnergyConsumerAttributionProto.UID,
+                                energyConsumerAttribution.uid);
+                        pos.write(EnergyConsumerAttributionProto.ENERGY_UWS,
+                                energyConsumerAttribution.energyUWs);
+                        pos.end(ecaToken);
+                    }
+                }
+
+                pos.end(ecrToken);
             }
         }
 
@@ -480,9 +498,45 @@
             }
         }
 
+        private static EnergyConsumerAttribution unpackEnergyConsumerAttributionProto(
+                ProtoInputStream pis) throws IOException {
+            final EnergyConsumerAttribution energyConsumerAttribution =
+                    new EnergyConsumerAttribution();
+
+            while (true) {
+                try {
+                    switch (pis.nextField()) {
+                        case (int) EnergyConsumerAttributionProto.UID:
+                            energyConsumerAttribution.uid =
+                                pis.readInt(EnergyConsumerAttributionProto.UID);
+                            break;
+
+                        case (int) EnergyConsumerAttributionProto.ENERGY_UWS:
+                            energyConsumerAttribution.energyUWs =
+                                pis.readLong(EnergyConsumerAttributionProto.ENERGY_UWS);
+                            break;
+
+                        case ProtoInputStream.NO_MORE_FIELDS:
+                            return energyConsumerAttribution;
+
+                        default:
+                            Slog.e(TAG, "Unhandled field in EnergyConsumerAttributionProto: "
+                                    + ProtoUtils.currentFieldToString(pis));
+                            break;
+
+                    }
+                } catch (WireTypeMismatchException wtme) {
+                    Slog.e(TAG, "Wire Type mismatch in EnergyConsumerAttributionProto: "
+                            + ProtoUtils.currentFieldToString(pis));
+                }
+            }
+        }
+
         private static EnergyConsumerResult unpackEnergyConsumerResultProto(ProtoInputStream pis)
                 throws IOException {
             EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
+            final List<EnergyConsumerAttribution> energyConsumerAttributionList =
+                    new ArrayList<EnergyConsumerAttribution>();
 
             while (true) {
                 try {
@@ -501,7 +555,18 @@
                                 pis.readLong(EnergyConsumerResultProto.ENERGY_UWS);
                             break;
 
+                        case (int) EnergyConsumerResultProto.ATTRIBUTION:
+                            final long token = pis.start(EnergyConsumerResultProto.ATTRIBUTION);
+                            energyConsumerAttributionList.add(
+                                    unpackEnergyConsumerAttributionProto(pis));
+                            pis.end(token);
+                            break;
+
                         case ProtoInputStream.NO_MORE_FIELDS:
+                            energyConsumerResult.attribution =
+                                energyConsumerAttributionList.toArray(
+                                    new EnergyConsumerAttribution[
+                                        energyConsumerAttributionList.size()]);
                             return energyConsumerResult;
 
                         default:
@@ -520,9 +585,16 @@
             if (energyConsumerResult == null) return;
 
             for (int i = 0; i < energyConsumerResult.length; i++) {
-                Slog.d(TAG, "EnergyConsumerId: " + energyConsumerResult[i].id
-                        + ", Timestamp (ms): " + energyConsumerResult[i].timestampMs
-                        + ", Energy (uWs): " + energyConsumerResult[i].energyUWs);
+                final EnergyConsumerResult result = energyConsumerResult[i];
+                Slog.d(TAG, "EnergyConsumerId: " + result.id
+                        + ", Timestamp (ms): " + result.timestampMs
+                        + ", Energy (uWs): " + result.energyUWs);
+                final int attributionLength = result.attribution.length;
+                for (int j = 0; j < attributionLength; j++) {
+                    final EnergyConsumerAttribution attribution = result.attribution[j];
+                    Slog.d(TAG, "  UID: " + attribution.uid
+                            + "  Energy (uWs): " + attribution.energyUWs);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
index f8b9601..7c6999a 100644
--- a/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
+++ b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
@@ -70,7 +70,7 @@
     }
 
     private int pullOnDevicePowerMeasurement(int atomTag, List<StatsEvent> events) {
-        EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeters(new int[0]);
+        EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeter(new int[0]);
         if (energyMeasurements == null) {
             return StatsManager.PULL_SKIP;
         }
diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java
index 7cba00f..f8a4135 100644
--- a/services/core/java/com/android/server/powerstats/TimerTrigger.java
+++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java
@@ -29,18 +29,30 @@
     private static final String TAG = TimerTrigger.class.getSimpleName();
     private static final boolean DEBUG = false;
     // TODO(b/166689029): Make configurable through global settings.
-    private static final long LOG_PERIOD_MS = 120 * 1000;
+    private static final long LOG_PERIOD_MS_LOW_FREQUENCY = 60 * 60 * 1000; // 1 hour
+    private static final long LOG_PERIOD_MS_HIGH_FREQUENCY = 2 * 60 * 1000; // 2 minutes
 
     private final Handler mHandler;
 
-    private Runnable mLogData = new Runnable() {
+    private Runnable mLogDataLowFrequency = new Runnable() {
         @Override
         public void run() {
             // Do not wake the device for these messages.  Opportunistically log rail data every
-            // LOG_PERIOD_MS.
-            mHandler.postDelayed(mLogData, LOG_PERIOD_MS);
-            if (DEBUG) Slog.d(TAG, "Received delayed message.  Log rail data");
-            logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER);
+            // LOG_PERIOD_MS_LOW_FREQUENCY.
+            mHandler.postDelayed(mLogDataLowFrequency, LOG_PERIOD_MS_LOW_FREQUENCY);
+            if (DEBUG) Slog.d(TAG, "Received delayed message.  Log rail data low frequency");
+            logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
+        }
+    };
+
+    private Runnable mLogDataHighFrequency = new Runnable() {
+        @Override
+        public void run() {
+            // Do not wake the device for these messages.  Opportunistically log rail data every
+            // LOG_PERIOD_MS_HIGH_FREQUENCY.
+            mHandler.postDelayed(mLogDataHighFrequency, LOG_PERIOD_MS_HIGH_FREQUENCY);
+            if (DEBUG) Slog.d(TAG, "Received delayed message.  Log rail data high frequency");
+            logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
         }
     };
 
@@ -50,7 +62,8 @@
         mHandler = mContext.getMainThreadHandler();
 
         if (triggerEnabled) {
-            mLogData.run();
+            mLogDataLowFrequency.run();
+            mLogDataHighFrequency.run();
         }
     }
 }
diff --git a/services/core/java/com/android/server/timedetector/DeviceConfig.java b/services/core/java/com/android/server/timedetector/DeviceConfig.java
new file mode 100644
index 0000000..7b9ad0f
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/DeviceConfig.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2021 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.timedetector;
+
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+
+import java.time.Duration;
+import java.util.concurrent.Executor;
+
+/**
+ * A helper class for reading / monitoring the {@link
+ * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace for server-configured flags.
+ */
+public final class DeviceConfig {
+
+    /**
+     * An annotation used to indicate when a {@link
+     * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} key is required.
+     *
+     * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider
+     * also shares the {@link android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the
+     * prefix "geotz_" on all of its key strings.
+     */
+    @StringDef(prefix = "KEY_", value = {
+            KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED,
+            KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT,
+            KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
+            KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+            KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+    })
+    @interface DeviceConfigKey {}
+
+    /**
+     * The key to force location time zone detection on for a device. Only intended for use during
+     * release testing with droidfooders. The user can still disable the feature by turning off the
+     * master location switch, or disabling automatic time zone detection.
+     */
+    @DeviceConfigKey
+    public static final String KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED =
+            "force_location_time_zone_detection_enabled";
+
+    /**
+     * The key for the default value used to determine whether location time zone detection is
+     * enabled when the user hasn't explicitly set it yet.
+     */
+    @DeviceConfigKey
+    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT =
+            "location_time_zone_detection_enabled_default";
+
+    /**
+     * The key for the minimum delay after location time zone detection has been enabled before the
+     * location time zone manager can report it is uncertain about the time zone.
+     */
+    @DeviceConfigKey
+    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS =
+            "location_time_zone_detection_uncertainty_delay_millis";
+
+    /**
+     * The key for the timeout passed to a location time zone provider that tells it how long it has
+     * to provide an explicit first suggestion without being declared uncertain.
+     */
+    @DeviceConfigKey
+    public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS =
+            "ltpz_init_timeout_millis";
+
+    /**
+     * The key for the extra time added to {@link
+     * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone
+     * manager before the location time zone provider will actually be declared uncertain.
+     */
+    @DeviceConfigKey
+    public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
+            "ltpz_init_timeout_fuzz_millis";
+
+    /** Creates an instance. */
+    public DeviceConfig() {}
+
+    /** Adds a listener for the system_time namespace. */
+    public void addListener(
+            @NonNull Executor handlerExecutor, @NonNull Runnable listener) {
+        android.provider.DeviceConfig.addOnPropertiesChangedListener(
+                NAMESPACE_SYSTEM_TIME,
+                handlerExecutor,
+                properties -> listener.run());
+    }
+
+    /**
+     * Returns a boolean value from {@link android.provider.DeviceConfig} from the system_time
+     * namespace, or {@code defaultValue} if there is no explicit value set.
+     */
+    public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) {
+        return android.provider.DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue);
+    }
+
+    /**
+     * Returns a positive duration from {@link android.provider.DeviceConfig} from the system_time
+     * namespace, or {@code defaultValue} if there is no explicit value set.
+     */
+    @Nullable
+    public Duration getDurationFromMillis(
+            @DeviceConfigKey String key, @Nullable Duration defaultValue) {
+        long deviceConfigValue =
+                android.provider.DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1);
+        if (deviceConfigValue < 0) {
+            return defaultValue;
+        }
+        return Duration.ofMillis(deviceConfigValue);
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index 3340792..f52b9b1 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -33,15 +33,19 @@
 import android.location.LocationManager;
 import android.net.ConnectivityManager;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
+import com.android.server.timedetector.DeviceConfig;
 
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
@@ -55,21 +59,26 @@
     @NonNull private final Handler mHandler;
     @NonNull private final ContentResolver mCr;
     @NonNull private final UserManager mUserManager;
+    @NonNull private final DeviceConfig mDeviceConfig;
     @NonNull private final boolean mGeoDetectionSupported;
     @NonNull private final LocationManager mLocationManager;
+
     // @NonNull after setConfigChangeListener() is called.
+    @GuardedBy("this")
     private ConfigurationChangeListener mConfigChangeListener;
 
     EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
-            boolean geoDetectionSupported) {
+            @NonNull DeviceConfig deviceConfig, boolean geoDetectionSupported) {
         mContext = Objects.requireNonNull(context);
         mHandler = Objects.requireNonNull(handler);
+        Executor handlerExecutor = new HandlerExecutor(mHandler);
         mCr = context.getContentResolver();
         mUserManager = context.getSystemService(UserManager.class);
         mLocationManager = context.getSystemService(LocationManager.class);
+        mDeviceConfig = deviceConfig;
         mGeoDetectionSupported = geoDetectionSupported;
 
-        // Wire up the change listener. All invocations are performed on the mHandler thread.
+        // Wire up the change listeners. All invocations are performed on the mHandler thread.
 
         // Listen for the user changing / the user's location mode changing.
         IntentFilter filter = new IntentFilter();
@@ -103,18 +112,29 @@
                         handleConfigChangeOnHandlerThread();
                     }
                 }, UserHandle.USER_ALL);
+
+        // Add async callbacks for changes to server-side flags: some of the flags affect device /
+        // user config. All changes can be treated like a config change. If flags that affect config
+        // haven't changed then call will be a no-op.
+        mDeviceConfig.addListener(
+                handlerExecutor,
+                this::handleConfigChangeOnHandlerThread);
     }
 
     private void handleConfigChangeOnHandlerThread() {
-        if (mConfigChangeListener == null) {
-            Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
+        synchronized (this) {
+            if (mConfigChangeListener == null) {
+                Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
+            }
+            mConfigChangeListener.onChange();
         }
-        mConfigChangeListener.onChange();
     }
 
     @Override
     public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
-        mConfigChangeListener = Objects.requireNonNull(listener);
+        synchronized (this) {
+            mConfigChangeListener = Objects.requireNonNull(listener);
+        }
     }
 
     @Override
@@ -174,7 +194,10 @@
             // time zone detection: if we wrote it down then we'd set the value explicitly, which
             // would prevent detecting "default" later. That might influence what happens on later
             // releases that support geo detection on the same hardware.
-            if (isGeoDetectionSupported()) {
+            // Also avoid writing the geo detection enabled setting for devices that are currently
+            // force-enabled: otherwise we might overwrite a droidfood user's real setting
+            // permanently.
+            if (isGeoDetectionSupported() && !isGeoDetectionForceEnabled()) {
                 final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
                 setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled);
             }
@@ -213,12 +236,25 @@
     }
 
     private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
-        final boolean geoDetectionEnabledByDefault = false;
+        // We may never use this, but it gives us a way to force location-based time zone detection
+        // on for testers (where their other settings allow).
+        boolean forceEnabled = isGeoDetectionForceEnabled();
+        if (forceEnabled) {
+            return true;
+        }
+
+        final boolean geoDetectionEnabledByDefault = mDeviceConfig.getBoolean(
+                DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT, false);
         return Settings.Secure.getIntForUser(mCr,
                 Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
                 (geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0;
     }
 
+    private boolean isGeoDetectionForceEnabled() {
+        return mDeviceConfig.getBoolean(
+                DeviceConfig.KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED, false);
+    }
+
     private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) {
         // See comment in setAutoDetectionEnabledIfRequired. http://b/171953500
         if (isGeoDetectionEnabled(userId) != enabled) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index cac1a6d..c464b74 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -38,6 +38,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.timedetector.DeviceConfig;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -203,10 +204,11 @@
      */
     public static TimeZoneDetectorStrategyImpl create(
             @NonNull Context context, @NonNull Handler handler,
-            boolean geolocationTimeZoneDetectionSupported) {
+            boolean geoDetectionSupported) {
 
+        DeviceConfig deviceConfig = new DeviceConfig();
         EnvironmentImpl environment = new EnvironmentImpl(
-                context, handler, geolocationTimeZoneDetectionSupported);
+                context, handler, deviceConfig, geoDetectionSupported);
         return new TimeZoneDetectorStrategyImpl(environment);
     }
 
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
index 83b33ee..a54288f 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 
 import com.android.server.LocalServices;
+import com.android.server.timedetector.DeviceConfig;
 import com.android.server.timezonedetector.ConfigurationChangeListener;
 import com.android.server.timezonedetector.ConfigurationInternal;
 import com.android.server.timezonedetector.TimeZoneDetectorInternal;
@@ -32,18 +33,22 @@
  */
 class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
 
-    private static final Duration PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
-    private static final Duration PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = Duration.ofMinutes(1);
-    private static final Duration PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
+    private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
+    private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
+            Duration.ofMinutes(1);
+    private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
 
     @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
     @NonNull private final LocationTimeZoneProviderController mController;
+    @NonNull private final DeviceConfig mDeviceConfig;
     @NonNull private final ConfigurationChangeListener mConfigurationChangeListener;
 
     ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
+            @NonNull DeviceConfig deviceConfig,
             @NonNull LocationTimeZoneProviderController controller) {
         super(threadingDomain);
         mController = Objects.requireNonNull(controller);
+        mDeviceConfig = Objects.requireNonNull(deviceConfig);
         mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);
 
         // Listen for configuration changes.
@@ -66,18 +71,24 @@
     @Override
     @NonNull
     Duration getProviderInitializationTimeout() {
-        return PROVIDER_INITIALIZATION_TIMEOUT;
+        return mDeviceConfig.getDurationFromMillis(
+                DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+                DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
     }
 
     @Override
     @NonNull
     Duration getProviderInitializationTimeoutFuzz() {
-        return PROVIDER_INITIALIZATION_TIMEOUT_FUZZ;
+        return mDeviceConfig.getDurationFromMillis(
+                DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+                DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
     }
 
     @Override
     @NonNull
     Duration getUncertaintyDelay() {
-        return PROVIDER_UNCERTAINTY_DELAY;
+        return mDeviceConfig.getDurationFromMillis(
+                DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
+                DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
     }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 220810f..364eaf8 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -43,6 +43,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
+import com.android.server.timedetector.DeviceConfig;
 import com.android.server.timezonedetector.TimeZoneDetectorInternal;
 import com.android.server.timezonedetector.TimeZoneDetectorService;
 
@@ -227,10 +228,10 @@
                     LocationTimeZoneProvider secondary = createSecondaryProvider();
                     mLocationTimeZoneDetectorController =
                             new ControllerImpl(mThreadingDomain, primary, secondary);
-                    ControllerCallbackImpl callback = new ControllerCallbackImpl(
-                            mThreadingDomain);
+                    DeviceConfig deviceConfig = new DeviceConfig();
                     mEnvironment = new ControllerEnvironmentImpl(
-                            mThreadingDomain, mLocationTimeZoneDetectorController);
+                            mThreadingDomain, deviceConfig, mLocationTimeZoneDetectorController);
+                    ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
                     mLocationTimeZoneDetectorController.initialize(mEnvironment, callback);
                 }
             }
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index fd12c2d2..b6ddd93 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -28,16 +28,15 @@
 import android.net.TelephonyNetworkSpecifier;
 import android.os.Handler;
 import android.os.ParcelUuid;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 
-import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -55,19 +54,18 @@
 
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
+    @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
     @NonNull private final UnderlyingNetworkTrackerCallback mCb;
     @NonNull private final Dependencies mDeps;
     @NonNull private final Handler mHandler;
     @NonNull private final ConnectivityManager mConnectivityManager;
-    @NonNull private final SubscriptionManager mSubscriptionManager;
 
-    @NonNull private final SparseArray<NetworkCallback> mCellBringupCallbacks = new SparseArray<>();
+    @NonNull private final Map<Integer, NetworkCallback> mCellBringupCallbacks = new ArrayMap<>();
     @NonNull private final NetworkCallback mWifiBringupCallback = new NetworkBringupCallback();
     @NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback();
 
-    @NonNull private final Set<Integer> mSubIds = new ArraySet<>();
-
-    @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
+    @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
+    private boolean mIsRunning = true;
 
     @Nullable private UnderlyingNetworkRecord mCurrentRecord;
     @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
@@ -75,11 +73,13 @@
     public UnderlyingNetworkTracker(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot snapshot,
             @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
             @NonNull UnderlyingNetworkTrackerCallback cb) {
         this(
                 vcnContext,
                 subscriptionGroup,
+                snapshot,
                 requiredUnderlyingNetworkCapabilities,
                 cb,
                 new Dependencies());
@@ -88,11 +88,13 @@
     private UnderlyingNetworkTracker(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot snapshot,
             @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
             @NonNull UnderlyingNetworkTrackerCallback cb,
             @NonNull Dependencies deps) {
         mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+        mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
         mRequiredUnderlyingNetworkCapabilities =
                 Objects.requireNonNull(
                         requiredUnderlyingNetworkCapabilities,
@@ -103,7 +105,6 @@
         mHandler = new Handler(mVcnContext.getLooper());
 
         mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
-        mSubscriptionManager = mVcnContext.getContext().getSystemService(SubscriptionManager.class);
 
         registerNetworkRequests();
     }
@@ -149,34 +150,47 @@
     private void updateSubIdsAndCellularRequests() {
         mVcnContext.ensureRunningOnLooperThread();
 
-        Set<Integer> prevSubIds = new ArraySet<>(mSubIds);
-        mSubIds.clear();
-
-        // Ensure NetworkRequests filed for all current subIds in mSubscriptionGroup
-        // STOPSHIP: b/177364490 use TelephonySubscriptionSnapshot to avoid querying Telephony
-        List<SubscriptionInfo> subInfos =
-                mSubscriptionManager.getSubscriptionsInGroup(mSubscriptionGroup);
-
-        for (SubscriptionInfo subInfo : subInfos) {
-            final int subId = subInfo.getSubscriptionId();
-            mSubIds.add(subId);
-
-            if (!mCellBringupCallbacks.contains(subId)) {
-                final NetworkBringupCallback cb = new NetworkBringupCallback();
-                mCellBringupCallbacks.put(subId, cb);
-
-                mConnectivityManager.requestBackgroundNetwork(
-                        getCellNetworkRequestForSubId(subId), mHandler, cb);
-            }
+        // Don't bother re-filing NetworkRequests if this Tracker has been torn down.
+        if (!mIsRunning) {
+            return;
         }
 
-        // unregister all NetworkCallbacks for outdated subIds
-        for (final int subId : prevSubIds) {
-            if (!mSubIds.contains(subId)) {
-                final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
-                mConnectivityManager.unregisterNetworkCallback(cb);
-            }
+        final Set<Integer> subIdsInSubGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup);
+
+        // new subIds to track = (updated list of subIds) - (currently tracked subIds)
+        final Set<Integer> subIdsToRegister = new ArraySet<>(subIdsInSubGroup);
+        subIdsToRegister.removeAll(mCellBringupCallbacks.keySet());
+
+        // subIds to stop tracking = (currently tracked subIds) - (updated list of subIds)
+        final Set<Integer> subIdsToUnregister = new ArraySet<>(mCellBringupCallbacks.keySet());
+        subIdsToUnregister.removeAll(subIdsInSubGroup);
+
+        for (final int subId : subIdsToRegister) {
+            final NetworkBringupCallback cb = new NetworkBringupCallback();
+            mCellBringupCallbacks.put(subId, cb);
+
+            mConnectivityManager.requestBackgroundNetwork(
+                    getCellNetworkRequestForSubId(subId), mHandler, cb);
         }
+
+        for (final int subId : subIdsToUnregister) {
+            final NetworkCallback cb = mCellBringupCallbacks.remove(subId);
+            mConnectivityManager.unregisterNetworkCallback(cb);
+        }
+    }
+
+    /**
+     * Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot.
+     *
+     * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkTracker to
+     * reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered
+     * or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change.
+     */
+    public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+        Objects.requireNonNull(snapshot, "Missing snapshot");
+
+        mLastSnapshot = snapshot;
+        updateSubIdsAndCellularRequests();
     }
 
     /** Tears down this Tracker, and releases all underlying network requests. */
@@ -186,11 +200,12 @@
         mConnectivityManager.unregisterNetworkCallback(mWifiBringupCallback);
         mConnectivityManager.unregisterNetworkCallback(mRouteSelectionCallback);
 
-        for (final int subId : mSubIds) {
-            final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
+        for (final NetworkCallback cb : mCellBringupCallbacks.values()) {
             mConnectivityManager.unregisterNetworkCallback(cb);
         }
-        mSubIds.clear();
+        mCellBringupCallbacks.clear();
+
+        mIsRunning = false;
     }
 
     /** Returns whether the currently selected Network matches the given network. */
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 132883e..5ec527a 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -27,9 +27,18 @@
 import android.os.ParcelUuid;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Represents an single instance of a VCN.
@@ -63,41 +72,86 @@
      */
     private static final int MSG_EVENT_NETWORK_REQUESTED = MSG_EVENT_BASE + 1;
 
+    /**
+     * The TelephonySubscriptionSnapshot tracked by VcnManagementService has changed.
+     *
+     * <p>This updated snapshot should be cached locally and passed to all VcnGatewayConnections.
+     *
+     * @param obj TelephonySubscriptionSnapshot
+     */
+    private static final int MSG_EVENT_SUBSCRIPTIONS_CHANGED = MSG_EVENT_BASE + 2;
+
     /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
     private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
 
+    /**
+     * Causes this VCN to immediately enter Safemode.
+     *
+     * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its
+     * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode.
+     */
+    private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1;
+
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
     @NonNull private final Dependencies mDeps;
     @NonNull private final VcnNetworkRequestListener mRequestListener;
+    @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback;
 
     @NonNull
     private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
             new HashMap<>();
 
     @NonNull private VcnConfig mConfig;
+    @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
 
-    private boolean mIsRunning = true;
+    /**
+     * Whether this Vcn instance is active and running.
+     *
+     * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been
+     * shut down or has entered safe mode.
+     *
+     * <p>This AtomicBoolean is required in order to ensure consistency and correctness across
+     * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads
+     * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
+     * Looper.
+     */
+    // TODO(b/179429339): update when exiting safemode (when a new VcnConfig is provided)
+    private final AtomicBoolean mIsActive = new AtomicBoolean(true);
 
     public Vcn(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
-            @NonNull VcnConfig config) {
-        this(vcnContext, subscriptionGroup, config, new Dependencies());
+            @NonNull VcnConfig config,
+            @NonNull TelephonySubscriptionSnapshot snapshot,
+            @NonNull VcnSafemodeCallback vcnSafemodeCallback) {
+        this(
+                vcnContext,
+                subscriptionGroup,
+                config,
+                snapshot,
+                vcnSafemodeCallback,
+                new Dependencies());
     }
 
-    private Vcn(
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public Vcn(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
             @NonNull VcnConfig config,
+            @NonNull TelephonySubscriptionSnapshot snapshot,
+            @NonNull VcnSafemodeCallback vcnSafemodeCallback,
             @NonNull Dependencies deps) {
         super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
         mVcnContext = vcnContext;
         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+        mVcnSafemodeCallback =
+                Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback");
         mDeps = Objects.requireNonNull(deps, "Missing deps");
         mRequestListener = new VcnNetworkRequestListener();
 
         mConfig = Objects.requireNonNull(config, "Missing config");
+        mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
 
         // Register to receive cached and future NetworkRequests
         mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
@@ -110,11 +164,29 @@
         sendMessage(obtainMessage(MSG_EVENT_CONFIG_UPDATED, config));
     }
 
+    /** Asynchronously updates the Subscription snapshot for this VCN. */
+    public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+        Objects.requireNonNull(snapshot, "Missing snapshot");
+
+        sendMessage(obtainMessage(MSG_EVENT_SUBSCRIPTIONS_CHANGED, snapshot));
+    }
+
     /** Asynchronously tears down this Vcn instance, including VcnGatewayConnection(s) */
     public void teardownAsynchronously() {
         sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
     }
 
+    /** Synchronously checks whether this Vcn is active. */
+    public boolean isActive() {
+        return mIsActive.get();
+    }
+
+    /** Get current Gateways for testing purposes */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public Set<VcnGatewayConnection> getVcnGatewayConnections() {
+        return Collections.unmodifiableSet(new HashSet<>(mVcnGatewayConnections.values()));
+    }
+
     private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener {
         @Override
         public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
@@ -126,7 +198,7 @@
 
     @Override
     public void handleMessage(@NonNull Message msg) {
-        if (!mIsRunning) {
+        if (!isActive()) {
             return;
         }
 
@@ -137,9 +209,15 @@
             case MSG_EVENT_NETWORK_REQUESTED:
                 handleNetworkRequested((NetworkRequest) msg.obj, msg.arg1, msg.arg2);
                 break;
+            case MSG_EVENT_SUBSCRIPTIONS_CHANGED:
+                handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj);
+                break;
             case MSG_CMD_TEARDOWN:
                 handleTeardown();
                 break;
+            case MSG_CMD_ENTER_SAFEMODE:
+                handleEnterSafemode();
+                break;
             default:
                 Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
         }
@@ -161,7 +239,13 @@
             gatewayConnection.teardownAsynchronously();
         }
 
-        mIsRunning = false;
+        mIsActive.set(false);
+    }
+
+    private void handleEnterSafemode() {
+        handleTeardown();
+
+        mVcnSafemodeCallback.onEnteredSafemode();
     }
 
     private void handleNetworkRequested(
@@ -192,13 +276,27 @@
                         "Bringing up new VcnGatewayConnection for request " + request.requestId);
 
                 final VcnGatewayConnection vcnGatewayConnection =
-                        new VcnGatewayConnection(
-                                mVcnContext, mSubscriptionGroup, gatewayConnectionConfig);
+                        mDeps.newVcnGatewayConnection(
+                                mVcnContext,
+                                mSubscriptionGroup,
+                                mLastSnapshot,
+                                gatewayConnectionConfig,
+                                new VcnGatewayStatusCallbackImpl());
                 mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
             }
         }
     }
 
+    private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
+        mLastSnapshot = snapshot;
+
+        if (isActive()) {
+            for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
+                gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
+            }
+        }
+    }
+
     private boolean requestSatisfiedByGatewayConnectionConfig(
             @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
@@ -214,11 +312,43 @@
     }
 
     /** Retrieves the network score for a VCN Network */
-    private int getNetworkScore() {
+    // Package visibility for use in VcnGatewayConnection
+    static int getNetworkScore() {
         // TODO: STOPSHIP (b/173549607): Make this use new NetworkSelection, or some magic "max in
         //                               subGrp" value
         return 52;
     }
 
-    private static class Dependencies {}
+    /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public interface VcnGatewayStatusCallback {
+        /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */
+        void onEnteredSafemode();
+    }
+
+    private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
+        @Override
+        public void onEnteredSafemode() {
+            sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE));
+        }
+    }
+
+    /** External dependencies used by Vcn, for injection in tests */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class Dependencies {
+        /** Builds a new VcnGatewayConnection */
+        public VcnGatewayConnection newVcnGatewayConnection(
+                VcnContext vcnContext,
+                ParcelUuid subscriptionGroup,
+                TelephonySubscriptionSnapshot snapshot,
+                VcnGatewayConnectionConfig connectionConfig,
+                VcnGatewayStatusCallback gatewayStatusCallback) {
+            return new VcnGatewayConnection(
+                    vcnContext,
+                    subscriptionGroup,
+                    snapshot,
+                    connectionConfig,
+                    gatewayStatusCallback);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 39c9606..9ecdf1b 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -17,8 +17,11 @@
 package com.android.server.vcn;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
 import static com.android.server.VcnManagementService.VDBG;
 
@@ -34,8 +37,10 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.RouteInfo;
+import android.net.TelephonyNetworkSpecifier;
 import android.net.annotations.PolicyDirection;
 import android.net.ipsec.ike.ChildSessionCallback;
 import android.net.ipsec.ike.ChildSessionConfiguration;
@@ -47,23 +52,29 @@
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
 import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.ParcelUuid;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
 
 import java.io.IOException;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
+import java.util.Arrays;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -113,6 +124,9 @@
 public class VcnGatewayConnection extends StateMachine {
     private static final String TAG = VcnGatewayConnection.class.getSimpleName();
 
+    private static final int[] MERGED_CAPABILITIES =
+            new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING};
+
     private static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
     private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE;
 
@@ -359,6 +373,16 @@
      */
     private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8;
 
+    /**
+     * Sent when this VcnGatewayConnection is notified of a change in TelephonySubscriptions.
+     *
+     * <p>Relevant in all states.
+     *
+     * @param arg1 The "all" token; this signal is always honored.
+     */
+    // TODO(b/178426520): implement handling of this event
+    private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9;
+
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     @NonNull
     final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -379,10 +403,13 @@
     @NonNull
     final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
 
+    @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
+
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
     @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
     @NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
+    @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
     @NonNull private final Dependencies mDeps;
 
     @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
@@ -457,28 +484,43 @@
     public VcnGatewayConnection(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
-            @NonNull VcnGatewayConnectionConfig connectionConfig) {
-        this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies());
+            @NonNull TelephonySubscriptionSnapshot snapshot,
+            @NonNull VcnGatewayConnectionConfig connectionConfig,
+            @NonNull VcnGatewayStatusCallback gatewayStatusCallback) {
+        this(
+                vcnContext,
+                subscriptionGroup,
+                snapshot,
+                connectionConfig,
+                gatewayStatusCallback,
+                new Dependencies());
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     VcnGatewayConnection(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
+            @NonNull TelephonySubscriptionSnapshot snapshot,
             @NonNull VcnGatewayConnectionConfig connectionConfig,
+            @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
             @NonNull Dependencies deps) {
         super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
         mVcnContext = vcnContext;
         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
         mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
+        mGatewayStatusCallback =
+                Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
         mDeps = Objects.requireNonNull(deps, "Missing deps");
 
+        mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
+
         mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
 
         mUnderlyingNetworkTracker =
                 mDeps.newUnderlyingNetworkTracker(
                         mVcnContext,
                         subscriptionGroup,
+                        mLastSnapshot,
                         mConnectionConfig.getAllUnderlyingCapabilities(),
                         mUnderlyingNetworkTrackerCallback);
         mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
@@ -533,10 +575,28 @@
         mUnderlyingNetworkTracker.teardown();
     }
 
+    /**
+     * Notify this Gateway that subscriptions have changed.
+     *
+     * <p>This snapshot should be used to update any keepalive requests necessary for potential
+     * underlying Networks in this Gateway's subscription group.
+     */
+    public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+        Objects.requireNonNull(snapshot, "Missing snapshot");
+        mVcnContext.ensureRunningOnLooperThread();
+
+        mLastSnapshot = snapshot;
+        mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
+
+        sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
+    }
+
     private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback {
         @Override
         public void onSelectedUnderlyingNetworkChanged(
                 @Nullable UnderlyingNetworkRecord underlying) {
+            // TODO(b/179091925): Move the delayed-message handling to BaseState
+
             // If underlying is null, all underlying networks have been lost. Disconnect VCN after a
             // timeout.
             if (underlying == null) {
@@ -668,7 +728,8 @@
                 case EVENT_TRANSFORM_CREATED: // Fallthrough
                 case EVENT_SETUP_COMPLETED: // Fallthrough
                 case EVENT_DISCONNECT_REQUESTED: // Fallthrough
-                case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+                case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
+                case EVENT_SUBSCRIPTIONS_CHANGED:
                     logUnexpectedEvent(msg.what);
                     break;
                 default:
@@ -921,6 +982,8 @@
                     transitionTo(mDisconnectingState);
                     break;
                 case EVENT_SESSION_CLOSED:
+                    // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
+                    // message may not be posted again. Defer to ensure immediate shutdown.
                     deferMessage(msg);
 
                     transitionTo(mDisconnectingState);
@@ -941,7 +1004,108 @@
         }
     }
 
-    private abstract class ConnectedStateBase extends ActiveBaseState {}
+    private abstract class ConnectedStateBase extends ActiveBaseState {
+        protected void updateNetworkAgent(
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull NetworkAgent agent,
+                @NonNull ChildSessionConfiguration childConfig) {
+            final NetworkCapabilities caps =
+                    buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+            final LinkProperties lp =
+                    buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+
+            agent.sendNetworkCapabilities(caps);
+            agent.sendLinkProperties(lp);
+        }
+
+        protected NetworkAgent buildNetworkAgent(
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull ChildSessionConfiguration childConfig) {
+            final NetworkCapabilities caps =
+                    buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+            final LinkProperties lp =
+                    buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+
+            final NetworkAgent agent =
+                    new NetworkAgent(
+                            mVcnContext.getContext(),
+                            mVcnContext.getLooper(),
+                            TAG,
+                            caps,
+                            lp,
+                            Vcn.getNetworkScore(),
+                            new NetworkAgentConfig(),
+                            mVcnContext.getVcnNetworkProvider()) {
+                        @Override
+                        public void unwanted() {
+                            teardownAsynchronously();
+                        }
+                    };
+
+            agent.register();
+            agent.markConnected();
+
+            return agent;
+        }
+
+        protected void applyTransform(
+                int token,
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull Network underlyingNetwork,
+                @NonNull IpSecTransform transform,
+                int direction) {
+            try {
+                // TODO: Set underlying network of tunnel interface
+
+                // Transforms do not need to be persisted; the IkeSession will keep them alive
+                mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
+            } catch (IOException e) {
+                Slog.d(TAG, "Transform application failed for network " + token, e);
+                sessionLost(token, e);
+            }
+        }
+
+        protected void setupInterface(
+                int token,
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull ChildSessionConfiguration childConfig) {
+            setupInterface(token, tunnelIface, childConfig, null);
+        }
+
+        protected void setupInterface(
+                int token,
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull ChildSessionConfiguration childConfig,
+                @Nullable ChildSessionConfiguration oldChildConfig) {
+            try {
+                final Set<LinkAddress> newAddrs =
+                        new ArraySet<>(childConfig.getInternalAddresses());
+                final Set<LinkAddress> existingAddrs = new ArraySet<>();
+                if (oldChildConfig != null) {
+                    existingAddrs.addAll(oldChildConfig.getInternalAddresses());
+                }
+
+                final Set<LinkAddress> toAdd = new ArraySet<>();
+                toAdd.addAll(newAddrs);
+                toAdd.removeAll(existingAddrs);
+
+                final Set<LinkAddress> toRemove = new ArraySet<>();
+                toRemove.addAll(existingAddrs);
+                toRemove.removeAll(newAddrs);
+
+                for (LinkAddress address : toAdd) {
+                    tunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
+                }
+
+                for (LinkAddress address : toRemove) {
+                    tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength());
+                }
+            } catch (IOException e) {
+                Slog.d(TAG, "Adding address to tunnel failed for token " + token, e);
+                sessionLost(token, e);
+            }
+        }
+    }
 
     /**
      * Stable state representing a VCN that has a functioning connection to the mobility anchor.
@@ -951,7 +1115,89 @@
      */
     class ConnectedState extends ConnectedStateBase {
         @Override
-        protected void processStateMsg(Message msg) {}
+        protected void enterState() throws Exception {
+            // Successful connection, clear failed attempt counter
+            mFailedAttempts = 0;
+        }
+
+        @Override
+        protected void processStateMsg(Message msg) {
+            switch (msg.what) {
+                case EVENT_UNDERLYING_NETWORK_CHANGED:
+                    handleUnderlyingNetworkChanged(msg);
+                    break;
+                case EVENT_SESSION_CLOSED:
+                    // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
+                    // message may not be posted again. Defer to ensure immediate shutdown.
+                    deferMessage(msg);
+                    transitionTo(mDisconnectingState);
+                    break;
+                case EVENT_SESSION_LOST:
+                    transitionTo(mDisconnectingState);
+                    break;
+                case EVENT_TRANSFORM_CREATED:
+                    final EventTransformCreatedInfo transformCreatedInfo =
+                            (EventTransformCreatedInfo) msg.obj;
+
+                    applyTransform(
+                            mCurrentToken,
+                            mTunnelIface,
+                            mUnderlying.network,
+                            transformCreatedInfo.transform,
+                            transformCreatedInfo.direction);
+                    break;
+                case EVENT_SETUP_COMPLETED:
+                    mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
+
+                    setupInterfaceAndNetworkAgent(mCurrentToken, mTunnelIface, mChildConfig);
+                    break;
+                case EVENT_DISCONNECT_REQUESTED:
+                    handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+                    break;
+                default:
+                    logUnhandledMessage(msg);
+                    break;
+            }
+        }
+
+        private void handleUnderlyingNetworkChanged(@NonNull Message msg) {
+            final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+            mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+            if (mUnderlying == null) {
+                // Ignored for now; a new network may be coming up. If none does, the delayed
+                // NETWORK_LOST disconnect will be fired, and tear down the session + network.
+                return;
+            }
+
+            // mUnderlying assumed non-null, given check above.
+            // If network changed, migrate. Otherwise, update any existing networkAgent.
+            if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) {
+                mIkeSession.setNetwork(mUnderlying.network);
+            } else {
+                // oldUnderlying is non-null & underlying network itself has not changed
+                // (only network properties were changed).
+
+                // Network not yet set up, or child not yet connected.
+                if (mNetworkAgent != null && mChildConfig != null) {
+                    // If only network properties changed and agent is active, update properties
+                    updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+                }
+            }
+        }
+
+        protected void setupInterfaceAndNetworkAgent(
+                int token,
+                @NonNull IpSecTunnelInterface tunnelIface,
+                @NonNull ChildSessionConfiguration childConfig) {
+            setupInterface(token, tunnelIface, childConfig);
+
+            if (mNetworkAgent == null) {
+                mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
+            } else {
+                updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig);
+            }
+        }
     }
 
     /**
@@ -966,7 +1212,8 @@
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     static NetworkCapabilities buildNetworkCapabilities(
-            @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
+            @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
+            @Nullable UnderlyingNetworkRecord underlying) {
         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
 
         builder.addTransportType(TRANSPORT_CELLULAR);
@@ -978,6 +1225,52 @@
             builder.addCapability(cap);
         }
 
+        if (underlying != null) {
+            final NetworkCapabilities underlyingCaps = underlying.networkCapabilities;
+
+            // Mirror merged capabilities.
+            for (int cap : MERGED_CAPABILITIES) {
+                if (underlyingCaps.hasCapability(cap)) {
+                    builder.addCapability(cap);
+                }
+            }
+
+            // Set admin UIDs for ConnectivityDiagnostics use.
+            final int[] underlyingAdminUids = underlyingCaps.getAdministratorUids();
+            Arrays.sort(underlyingAdminUids); // Sort to allow contains check below.
+
+            final int[] adminUids;
+            if (underlyingCaps.getOwnerUid() > 0 // No owner UID specified
+                    && 0 > Arrays.binarySearch(// Owner UID not found in admin UID list.
+                            underlyingAdminUids, underlyingCaps.getOwnerUid())) {
+                adminUids = Arrays.copyOf(underlyingAdminUids, underlyingAdminUids.length + 1);
+                adminUids[adminUids.length - 1] = underlyingCaps.getOwnerUid();
+                Arrays.sort(adminUids);
+            } else {
+                adminUids = underlyingAdminUids;
+            }
+            builder.setAdministratorUids(adminUids);
+
+            // Set TransportInfo for SysUI use (never parcelled out of SystemServer).
+            if (underlyingCaps.hasTransport(TRANSPORT_WIFI)
+                    && underlyingCaps.getTransportInfo() instanceof WifiInfo) {
+                final WifiInfo wifiInfo = (WifiInfo) underlyingCaps.getTransportInfo();
+                builder.setTransportInfo(new VcnTransportInfo(wifiInfo));
+            } else if (underlyingCaps.hasTransport(TRANSPORT_CELLULAR)
+                    && underlyingCaps.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
+                final TelephonyNetworkSpecifier telNetSpecifier =
+                        (TelephonyNetworkSpecifier) underlyingCaps.getNetworkSpecifier();
+                builder.setTransportInfo(new VcnTransportInfo(telNetSpecifier.getSubscriptionId()));
+            } else {
+                Slog.wtf(
+                        TAG,
+                        "Unknown transport type or missing TransportInfo/NetworkSpecifier for"
+                                + " non-null underlying network");
+            }
+        }
+
+        // TODO: Make a VcnNetworkSpecifier, and match all underlying subscription IDs.
+
         return builder.build();
     }
 
@@ -1138,10 +1431,15 @@
         public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
                 VcnContext vcnContext,
                 ParcelUuid subscriptionGroup,
+                TelephonySubscriptionSnapshot snapshot,
                 Set<Integer> requiredUnderlyingNetworkCapabilities,
                 UnderlyingNetworkTrackerCallback callback) {
             return new UnderlyingNetworkTracker(
-                    vcnContext, subscriptionGroup, requiredUnderlyingNetworkCapabilities, callback);
+                    vcnContext,
+                    subscriptionGroup,
+                    snapshot,
+                    requiredUnderlyingNetworkCapabilities,
+                    callback);
         }
 
         /** Builds a new IkeSession. */
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index b9babae..fe4ea30 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -25,6 +25,9 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
 import java.util.Objects;
 import java.util.Set;
 
@@ -52,8 +55,13 @@
         super(context, looper, VcnNetworkProvider.class.getSimpleName());
     }
 
-    // Package-private
-    void registerListener(@NonNull NetworkRequestListener listener) {
+    /**
+     * Registers a NetworkRequestListener with this NetworkProvider.
+     *
+     * <p>Upon registering, the provided listener will receive all cached requests.
+     */
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public void registerListener(@NonNull NetworkRequestListener listener) {
         mListeners.add(listener);
 
         // Send listener all cached requests
@@ -62,8 +70,9 @@
         }
     }
 
-    // Package-private
-    void unregisterListener(@NonNull NetworkRequestListener listener) {
+    /** Unregisters the specified listener from receiving future NetworkRequests. */
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public void unregisterListener(@NonNull NetworkRequestListener listener) {
         mListeners.remove(listener);
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 5fe853a..771b712 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -54,6 +54,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -100,6 +101,17 @@
     }
 
     @Override
+    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+            throws RemoteException {
+        try {
+            return super.onTransact(code, data, reply, flags);
+        } catch (RuntimeException e) {
+            throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(
+                    "ActivityClientController", e);
+        }
+    }
+
+    @Override
     public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -963,7 +975,8 @@
             final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
             if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
                 r.mDisplayContent.mAppTransition.overridePendingAppTransition(
-                        packageName, enterAnim, exitAnim, null, null);
+                        packageName, enterAnim, exitAnim, null, null,
+                        r.mOverrideTaskTransition);
             }
         }
         Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 36c5037..f29b57f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
 import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -82,7 +81,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
 import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.res.Configuration.EMPTY;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -203,7 +201,6 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -273,7 +270,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
-import android.permission.PermissionManager;
 import android.service.dreams.DreamActivity;
 import android.service.dreams.DreamManagerInternal;
 import android.service.voice.IVoiceInteractionSession;
@@ -682,6 +678,7 @@
     boolean mRequestForceTransition;
 
     boolean mEnteringAnimation;
+    boolean mOverrideTaskTransition;
 
     boolean mAppStopped;
     // A hint to override the window specified rotation animation, or -1 to use the window specified
@@ -1365,7 +1362,6 @@
             return;
         }
         final boolean surfaceReady = w.isDrawn()  // Regular case
-                || w.mWinAnimator.mSurfaceDestroyDeferred  // The preserved surface is still ready.
                 || w.isDragResizeChanged();  // Waiting for relayoutWindow to call preserveSurface.
         final boolean needsLetterbox = surfaceReady && isLetterboxed(w);
         updateRoundedCorners(w);
@@ -1627,6 +1623,8 @@
             if (rotationAnimation >= 0) {
                 mRotationAnimationHint = rotationAnimation;
             }
+
+            mOverrideTaskTransition = options.getOverrideTaskTransition();
         }
 
         ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
@@ -3997,7 +3995,8 @@
                         pendingOptions.getCustomEnterResId(),
                         pendingOptions.getCustomExitResId(),
                         pendingOptions.getAnimationStartedListener(),
-                        pendingOptions.getAnimationFinishedListener());
+                        pendingOptions.getAnimationFinishedListener(),
+                        pendingOptions.getOverrideTaskTransition());
                 break;
             case ANIM_CLIP_REVEAL:
                 displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
@@ -6775,20 +6774,6 @@
         // layout traversals.
         mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
         getResolvedOverrideConfiguration().seq = mConfigurationSeq;
-
-        // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in
-        // size compat mode.
-        if (providesMaxBounds()) {
-            if (DEBUG_CONFIGURATION) {
-                ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s "
-                        + "due to letterboxing? %s mismatch with parent bounds? %s size compat "
-                        + "mode %s", getUid(),
-                        resolvedConfig.windowConfiguration.getBounds(), mLetterbox != null,
-                        !matchParentBounds(), inSizeCompatMode());
-            }
-            resolvedConfig.windowConfiguration
-                    .setMaxBounds(resolvedConfig.windowConfiguration.getBounds());
-        }
     }
 
     /**
@@ -6972,19 +6957,6 @@
         return super.getBounds();
     }
 
-    @Override
-    public boolean providesMaxBounds() {
-        // System and SystemUI should always be able to access the physical display bounds,
-        // so do not provide it with the overridden maximum bounds.
-        // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
-        if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
-                getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
-            return false;
-        }
-        // Max bounds should be sandboxed when this is letterboxed or in size compat mode.
-        return mLetterbox != null || !matchParentBounds() || inSizeCompatMode();
-    }
-
     @VisibleForTesting
     @Override
     Rect getAnimationBounds(int appRootTaskClipMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c6ed16c..79f8229 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1814,8 +1814,7 @@
     }
 
     private Task computeTargetTask() {
-        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
-                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+        if (mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
             // A new task should be created instead of using existing one.
             return null;
         } else if (mSourceRecord != null) {
@@ -2098,13 +2097,6 @@
             final ActivityRecord top = targetTask.performClearTaskForReuseLocked(mStartActivity,
                     mLaunchFlags);
 
-            // The above code can remove {@code reusedActivity} from the task, leading to the
-            // {@code ActivityRecord} removing its reference to the {@code Task}. The task
-            // reference is needed in the call below to {@link setTargetStackAndMoveToFrontIfNeeded}
-            if (targetTaskTop.getTask() == null) {
-                targetTask.addChild(targetTaskTop);
-            }
-
             if (top != null) {
                 if (top.isRootOfTask()) {
                     // Activity aliases may mean we use different intents for the top activity,
@@ -2512,7 +2504,9 @@
         // If bring to front is requested, and no result is requested and we have not been given
         // an explicit task to launch in to, and we can find a task that was started with this
         // same component, then instead of launching bring that one to the front.
-        putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
+        putIntoExistingTask &= !isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)
+                ? (mInTask == null && mStartActivity.resultTo == null)
+                : (mInTask == null);
         ActivityRecord intentActivity = null;
         if (putIntoExistingTask) {
             if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a97eb7f..2d6e9b2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4849,15 +4849,21 @@
         try {
             return super.onTransact(code, data, reply, flags);
         } catch (RuntimeException e) {
-            if (!(e instanceof SecurityException)) {
-                Slog.w(TAG, "Activity Task Manager onTransact aborts "
-                        + " UID:" + Binder.getCallingUid()
-                        + " PID:" + Binder.getCallingPid(), e);
-            }
-            throw e;
+            throw logAndRethrowRuntimeExceptionOnTransact(TAG, e);
         }
     }
 
+    /** Provides the full stack traces of non-security exception that occurs in onTransact. */
+    static RuntimeException logAndRethrowRuntimeExceptionOnTransact(String name,
+            RuntimeException e) {
+        if (!(e instanceof SecurityException)) {
+            Slog.w(TAG, name + " onTransact aborts"
+                    + " UID:" + Binder.getCallingUid()
+                    + " PID:" + Binder.getCallingPid(), e);
+        }
+        throw e;
+    }
+
     /**
      * Sets the corresponding {@link DisplayArea} information for the process global
      * configuration. To be called when we need to show IME on a different {@link DisplayArea}
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index fde0369..c589fea 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -39,7 +39,7 @@
 import android.os.Bundle;
 
 import com.android.internal.R;
-import com.android.server.policy.IconUtilities;
+import com.android.internal.util.ImageUtils;
 
 /** Displays an ongoing notification for a process displaying an alert window */
 class AlertWindowNotification {
@@ -54,7 +54,6 @@
     private final NotificationManager mNotificationManager;
     private final String mPackageName;
     private boolean mPosted;
-    private IconUtilities mIconUtilities;
 
     AlertWindowNotification(WindowManagerService service, String packageName) {
         mService = service;
@@ -63,7 +62,6 @@
                 (NotificationManager) mService.mContext.getSystemService(NOTIFICATION_SERVICE);
         mNotificationTag = CHANNEL_PREFIX + mPackageName;
         mRequestCode = sNextRequestCode++;
-        mIconUtilities = new IconUtilities(mService.mContext);
     }
 
     void post() {
@@ -126,8 +124,9 @@
 
         if (aInfo != null) {
             final Drawable drawable = pm.getApplicationIcon(aInfo);
-            if (drawable != null) {
-                final Bitmap bitmap = mIconUtilities.createIconBitmap(drawable);
+            int size = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
+            final Bitmap bitmap = ImageUtils.buildScaledBitmap(drawable, size, size);
+            if (bitmap != null) {
                 builder.setLargeIcon(bitmap);
             }
         }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 90070c8..5b685b4 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -90,6 +90,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import static com.android.server.wm.WindowManagerInternal.KeyguardExitAnimationStartListener;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
 
@@ -257,6 +258,7 @@
     private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
 
     private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
+    private KeyguardExitAnimationStartListener mKeyguardExitAnimationStartListener;
     private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
 
     private int mLastClipRevealMaxTranslation;
@@ -266,6 +268,7 @@
     private final boolean mLowRamRecentsEnabled;
 
     private final int mDefaultWindowAnimationStyleResId;
+    private boolean mOverrideTaskTransition;
 
     private RemoteAnimationController mRemoteAnimationController;
 
@@ -445,7 +448,7 @@
                 AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
 
         if (mRemoteAnimationController != null) {
-            mRemoteAnimationController.goodToGo();
+            mRemoteAnimationController.goodToGo(transit);
         }
         return redoLayout;
     }
@@ -508,6 +511,11 @@
         mListeners.remove(listener);
     }
 
+    void registerKeygaurdExitAnimationStartListener(
+            KeyguardExitAnimationStartListener listener) {
+        mKeyguardExitAnimationStartListener = listener;
+    }
+
     public void notifyAppTransitionFinishedLocked(IBinder token) {
         for (int i = 0; i < mListeners.size(); i++) {
             mListeners.get(i).onAppTransitionFinishedLocked(token);
@@ -971,7 +979,8 @@
             @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
             boolean freeform, WindowContainer container) {
 
-        if (mNextAppTransitionOverrideRequested && container.canCustomizeAppTransition()) {
+        if (mNextAppTransitionOverrideRequested
+                && (container.canCustomizeAppTransition() || mOverrideTaskTransition)) {
             mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
         }
 
@@ -1175,7 +1184,8 @@
     }
 
     void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
-            IRemoteCallback startedCallback, IRemoteCallback endedCallback) {
+            IRemoteCallback startedCallback, IRemoteCallback endedCallback,
+            boolean overrideTaskTransaction) {
         if (canOverridePendingAppTransition()) {
             clear();
             mNextAppTransitionOverrideRequested = true;
@@ -1185,6 +1195,7 @@
             postAnimationCallback();
             mNextAppTransitionCallback = startedCallback;
             mAnimationFinishedCallback = endedCallback;
+            mOverrideTaskTransition = overrideTaskTransaction;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 582aeb3..4575cf7 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -102,6 +102,7 @@
     private final DisplayContent mDisplayContent;
     private final WallpaperController mWallpaperControllerLocked;
     private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
+    private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400;
 
     private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
 
@@ -437,10 +438,14 @@
                 return adapter;
             }
         }
-        if (mRemoteAnimationDefinition == null) {
-            return null;
+        if (mRemoteAnimationDefinition != null) {
+            final RemoteAnimationAdapter adapter = mRemoteAnimationDefinition.getAdapter(
+                    transit, activityTypes);
+            if (adapter != null) {
+                return adapter;
+            }
         }
-        return mRemoteAnimationDefinition.getAdapter(transit, activityTypes);
+        return null;
     }
 
     /**
@@ -709,6 +714,13 @@
         applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
                 voiceInteraction);
 
+        for (int i = 0; i < openingApps.size(); ++i) {
+            openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
+        }
+        for (int i = 0; i < closingApps.size(); ++i) {
+            closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
+        }
+
         final AccessibilityController accessibilityController =
                 mDisplayContent.mWmService.mAccessibilityController;
         if (accessibilityController != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 1b20c44..61fe023 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,7 +25,7 @@
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
 import static android.view.Display.TYPE_INTERNAL;
-import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
+import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
@@ -35,8 +35,9 @@
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_GESTURES;
+import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
 import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
+import static android.view.ViewRootImpl.computeWindowBounds;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -128,7 +129,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.util.ArraySet;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -142,8 +142,6 @@
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewDebug;
-import android.view.WindowInsets.Side;
-import android.view.WindowInsets.Side.InsetsSide;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsController.Appearance;
@@ -1076,7 +1074,7 @@
                             rect.bottom = rect.top + getStatusBarHeight(displayFrames);
                         };
                 mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider);
-                mDisplayContent.setInsetProvider(ITYPE_TOP_GESTURES, win, frameProvider);
+                mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider);
                 mDisplayContent.setInsetProvider(ITYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider);
                 break;
             case TYPE_NAVIGATION_BAR:
@@ -1103,7 +1101,7 @@
                         (displayFrames, windowState, inOutFrame) ->
                                 inOutFrame.set(windowState.getFrame()));
 
-                mDisplayContent.setInsetProvider(ITYPE_BOTTOM_GESTURES, win,
+                mDisplayContent.setInsetProvider(ITYPE_BOTTOM_MANDATORY_GESTURES, win,
                         (displayFrames, windowState, inOutFrame) -> {
                             inOutFrame.top -= mBottomGestureAdditionalInset;
                         });
@@ -1395,29 +1393,15 @@
      *
      * @param attrs The LayoutParams of the window.
      * @param windowToken The token of the window.
-     * @param outFrame The frame of the window.
      * @param outInsetsState The insets state of this display from the client's perspective.
      * @param localClient Whether the client is from the our process.
      * @return Whether to always consume the system bars.
      *         See {@link #areSystemBarsForcedShownLw(WindowState)}.
      */
-    boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, Rect outFrame,
-            InsetsState outInsetsState, boolean localClient) {
-        final boolean isFixedRotationTransforming =
-                windowToken != null && windowToken.isFixedRotationTransforming();
-        final ActivityRecord activity = windowToken != null ? windowToken.asActivityRecord() : null;
-        final Task task = activity != null ? activity.getTask() : null;
-        final Rect taskBounds = isFixedRotationTransforming
-                // Use token (activity) bounds if it is rotated because its task is not rotated.
-                ? windowToken.getBounds()
-                : (task != null ? task.getBounds() : null);
+    boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, InsetsState outInsetsState,
+            boolean localClient) {
         final InsetsState state =
                 mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
-        computeWindowBounds(attrs, state, windowToken, outFrame);
-        if (taskBounds != null) {
-            outFrame.intersect(taskBounds);
-        }
-
         final boolean inSizeCompatMode = WindowState.inSizeCompatMode(attrs, windowToken);
         outInsetsState.set(state, inSizeCompatMode || localClient);
         if (inSizeCompatMode) {
@@ -1558,28 +1542,6 @@
         return !notFocusableForIm;
     }
 
-    private void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state,
-            @Nullable WindowToken windowToken, Rect outBounds) {
-        final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
-        final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
-        final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
-        final Rect df = windowToken != null ? windowToken.getBounds() : state.getDisplayFrame();
-        Insets insets = Insets.of(0, 0, 0, 0);
-        for (int i = types.size() - 1; i >= 0; i--) {
-            final InsetsSource source = state.peekSource(types.valueAt(i));
-            if (source == null) {
-                continue;
-            }
-            insets = Insets.max(insets, source.calculateInsets(
-                    df, attrs.isFitInsetsIgnoringVisibility()));
-        }
-        final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
-        final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
-        final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0;
-        final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;
-        outBounds.set(df.left + left, df.top + top, df.right - right, df.bottom - bottom);
-    }
-
     /**
      * Called for each window attached to the window manager as layout is proceeding. The
      * implementation of this function must take care of setting the window's frame, either here or
@@ -1620,7 +1582,7 @@
         final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
 
         final InsetsState state = win.getInsetsState();
-        computeWindowBounds(attrs, state, win.mToken, df);
+        computeWindowBounds(attrs, state, win.mToken.getBounds(), df);
         if (attached == null) {
             pf.set(df);
             if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 2274a4a..99c9e79 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -340,7 +340,7 @@
         }
 
         public void applySurfaceChanges(SurfaceControl.Transaction t) {
-            if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) {
+            if (!needsApplySurfaceChanges()) {
                 // Nothing changed.
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e1fdefd..e02cce4 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -421,7 +421,10 @@
             if (skipAnimation(task)) {
                 continue;
             }
-            addAnimation(task, !recentTaskIds.get(task.mTaskId));
+            addAnimation(task, !recentTaskIds.get(task.mTaskId), false /* hidden */,
+                    (type, anim) -> task.forAllWindows(win -> {
+                        win.onAnimationFinished(type, anim);
+                    }, true /* traverseTopToBottom */));
         }
 
         // Skip the animation if there is nothing to animate
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index a1d2072..392f27e 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -36,6 +36,7 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
 
 import com.android.internal.protolog.ProtoLogImpl;
 import com.android.internal.protolog.common.ProtoLog;
@@ -98,7 +99,7 @@
     /**
      * Called when the transition is ready to be started, and all leashes have been set up.
      */
-    void goodToGo() {
+    void goodToGo(@WindowManager.TransitionOldType int transit) {
         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
         if (mPendingAnimations.isEmpty() || mCanceled) {
             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
@@ -123,11 +124,15 @@
 
         // Create the remote wallpaper animation targets (if any)
         final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
+
+        // TODO(bc-unlock): Create the remote non app animation targets (if any)
+        final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
+
         mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             try {
                 linkToDeathOfRunner();
-                mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
-                        mFinishedCallback);
+                mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
+                        wallpaperTargets, nonAppTargets, mFinishedCallback);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to start remote animation", e);
                 onAnimationFinished();
@@ -274,6 +279,7 @@
     private void setRunningRemoteAnimation(boolean running) {
         final int pid = mRemoteAnimationAdapter.getCallingPid();
         final int uid = mRemoteAnimationAdapter.getCallingUid();
+
         if (pid == 0) {
             throw new RuntimeException("Calling pid of remote animation was null");
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bbf6c76..2c97f78 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -37,6 +37,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
+import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
 import static android.view.WindowManager.TRANSIT_NONE;
@@ -932,7 +933,6 @@
                     displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                 }
                 win.destroySurfaceUnchecked();
-                win.mWinAnimator.destroyPreservedSurfaceLocked(win.getSyncTransaction());
             } while (i > 0);
             mWmService.mDestroySurface.clear();
         }
@@ -2180,6 +2180,8 @@
                 // display area, so reparent.
                 rootTask.reparent(taskDisplayArea, true /* onTop */);
             }
+            mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CHANGE, rootTask);
+
             // Defer the windowing mode change until after the transition to prevent the activity
             // from doing work and changing the activity visuals while animating
             // TODO(task-org): Figure-out more structured way to do this long term.
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 6df4536..6567195 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -253,6 +253,20 @@
             throw new SecurityException(msg);
         }
 
+        // Check if the caller is allowed to override any app transition animation.
+        final boolean overrideTaskTransition = options.getOverrideTaskTransition();
+        if (aInfo != null && overrideTaskTransition) {
+            final int startTasksFromRecentsPerm = ActivityTaskManagerService.checkPermission(
+                    START_TASKS_FROM_RECENTS, callingPid, callingUid);
+            if (startTasksFromRecentsPerm != PERMISSION_GRANTED) {
+                final String msg = "Permission Denial: starting " + getIntentString(intent)
+                        + " from " + callerApp + " (pid=" + callingPid
+                        + ", uid=" + callingUid + ") with overrideTaskTransition=true";
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+        }
+
         // Check permission for remote animations
         final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
         if (adapter != null && supervisor.mService.checkPermission(
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index fe7ec16..1f8daf6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -192,32 +192,30 @@
 
     @Override
     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
+            int viewVisibility, int displayId, InsetsState requestedVisibility,
             InputChannel outInputChannel, InsetsState outInsetsState,
             InsetsSourceControl[] outActiveControls) {
         return mService.addWindow(this, window, attrs, viewVisibility, displayId,
-                UserHandle.getUserId(mUid), requestedVisibility, outFrame, outInputChannel,
-                outInsetsState, outActiveControls);
+                UserHandle.getUserId(mUid), requestedVisibility, outInputChannel, outInsetsState,
+                outActiveControls);
     }
 
 
     @Override
     public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
-            Rect outFrame, InputChannel outInputChannel, InsetsState outInsetsState,
+            InputChannel outInputChannel, InsetsState outInsetsState,
             InsetsSourceControl[] outActiveControls) {
         return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
-                requestedVisibility, outFrame, outInputChannel, outInsetsState,
-                outActiveControls);
+                requestedVisibility, outInputChannel, outInsetsState, outActiveControls);
     }
 
     @Override
     public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, InsetsState outInsetsState) {
         return mService.addWindow(this, window, attrs, viewVisibility, displayId,
-                UserHandle.getUserId(mUid), mDummyRequestedVisibility,
-                new Rect() /* outFrame */, null /* outInputChannel */, outInsetsState,
-                mDummyControls);
+                UserHandle.getUserId(mUid), mDummyRequestedVisibility, null /* outInputChannel */,
+                outInsetsState, mDummyControls);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 57ba915..e44a028 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -77,7 +77,6 @@
 import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
 import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -145,7 +144,6 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.TASK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -921,10 +919,8 @@
             return;
         }
 
-        if (isLeafTask()) {
-            // This task is going away, so save the last state if necessary.
-            saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
-        }
+        // This task is going away, so save the last state if necessary.
+        saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
 
         // TODO: VI what about activity?
         final boolean isVoiceSession = voiceSession != null;
@@ -2319,12 +2315,9 @@
             return;
         }
 
-        final boolean windowingModeChanged = prevWindowingMode != getWindowingMode();
-        final int overrideWindowingMode = getRequestedOverrideWindowingMode();
-        // Update bounds if applicable
-        boolean hasNewOverrideBounds = false;
         // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
-        if ((overrideWindowingMode != WINDOWING_MODE_PINNED)
+        final int overrideWindowingMode = getRequestedOverrideWindowingMode();
+        if (overrideWindowingMode != WINDOWING_MODE_PINNED
                 && !getRequestedOverrideBounds().isEmpty()) {
             // If the parent (display) has rotated, rotate our bounds to best-fit where their
             // bounds were on the pre-rotated display.
@@ -2334,22 +2327,10 @@
                 mDisplayContent.rotateBounds(
                         newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation,
                         newBounds);
-                hasNewOverrideBounds = true;
+                setBounds(newBounds);
             }
         }
 
-        if (windowingModeChanged) {
-            taskDisplayArea.onRootTaskWindowingModeChanged(this);
-        }
-        if (hasNewOverrideBounds) {
-            if (inSplitScreenWindowingMode()) {
-                setBounds(newBounds);
-            } else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
-                // For root pinned task, resize is now part of the {@link
-                // WindowContainerTransaction}
-                resize(new Rect(newBounds), PRESERVE_WINDOWS, true /* deferResume */);
-            }
-        }
         if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
             // Since always on top is only on when the root task is freeform or pinned, the state
             // can be toggled when the windowing mode changes. We must make sure the root task is
@@ -2474,14 +2455,16 @@
 
     /**
      * Saves launching state if necessary so that we can launch the activity to its latest state.
-     * It only saves state if this task has been shown to user and it's in fullscreen or freeform
-     * mode on freeform displays.
      */
     private void saveLaunchingStateIfNeeded() {
         saveLaunchingStateIfNeeded(getDisplayContent());
     }
 
     private void saveLaunchingStateIfNeeded(DisplayContent display) {
+        if (!isLeafTask()) {
+            return;
+        }
+
         if (!getHasBeenVisible()) {
             // Not ever visible to user.
             return;
@@ -2882,16 +2865,6 @@
         // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
         outBounds.setEmpty();
         computeLetterboxBounds(outBounds, newParentConfig);
-        // Since the task is letterboxed due to mismatched orientation against its parent,
-        // sandbox max bounds to the app bounds.
-        if (!outBounds.isEmpty()) {
-            if (DEBUG_CONFIGURATION) {
-                ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds due to mismatched "
-                                + "orientation with parent, to %s vs DisplayArea %s", outBounds,
-                        getDisplayArea() != null ? getDisplayArea().getBounds() : "null");
-            }
-            getResolvedOverrideConfiguration().windowConfiguration.setMaxBounds(outBounds);
-        }
     }
 
     /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 3bd11ba..e18219e 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -23,7 +23,6 @@
 import android.content.ComponentName;
 import android.os.Binder;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteCallbackList;
@@ -52,15 +51,14 @@
     private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17;
     private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18;
     private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
-    private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20;
-    private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21;
-    private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 22;
-    private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 23;
-    private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 24;
-    private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 25;
-    private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 26;
-    private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 27;
-    private static final int NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG = 28;
+    private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 20;
+    private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 21;
+    private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 22;
+    private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 23;
+    private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 24;
+    private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 25;
+    private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 26;
+    private static final int NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG = 27;
 
     // Delay in notifying task stack change listeners (in millis)
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -151,10 +149,6 @@
         l.onTaskSnapshotChanged(m.arg1, (TaskSnapshot) m.obj);
     };
 
-    private final TaskStackConsumer mOnSizeCompatModeActivityChanged = (l, m) -> {
-        l.onSizeCompatModeActivityChanged(m.arg1, (IBinder) m.obj);
-    };
-
     private final TaskStackConsumer mNotifyTaskDisplayChanged = (l, m) -> {
         l.onTaskDisplayChanged(m.arg1, m.arg2);
     };
@@ -250,9 +244,6 @@
                 case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG:
                     forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg);
                     break;
-                case NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG:
-                    forAllRemoteListeners(mOnSizeCompatModeActivityChanged, msg);
-                    break;
                 case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
                     forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
                     break;
@@ -490,17 +481,6 @@
     }
 
     /**
-     * Notify listeners that whether a size compatibility mode activity is using the override
-     * bounds which is not fit its parent.
-     */
-    void notifySizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
-        final Message msg = mHandler.obtainMessage(NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG,
-                displayId, 0 /* unused */, activityToken);
-        forAllLocalListeners(mOnSizeCompatModeActivityChanged, msg);
-        msg.sendToTarget();
-    }
-
-    /**
      * Notify listeners that an activity received a back press when there are no other activities
      * in the back stack.
      */
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4185407..63732d8 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -47,6 +47,7 @@
 import android.os.UserHandle;
 import android.util.IntArray;
 import android.util.Slog;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.window.WindowContainerTransaction;
 
@@ -905,6 +906,13 @@
         }
     }
 
+    @Override
+    RemoteAnimationTarget createRemoteAnimationTarget(
+            RemoteAnimationController.RemoteAnimationRecord record) {
+        final ActivityRecord activity = getTopMostActivity();
+        return activity != null ? activity.createRemoteAnimationTarget(record) : null;
+    }
+
     SurfaceControl getSplitScreenDividerAnchor() {
         return mSplitScreenDividerAnchor;
     }
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index b3e0108..9fac3f0 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
 import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
@@ -33,6 +34,7 @@
 import android.content.pm.ParceledListSlice;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.Parcel;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.view.SurfaceControl;
@@ -357,8 +359,14 @@
         mGlobalLock = atm.mGlobalLock;
     }
 
-    private void enforceTaskPermission(String func) {
-        mService.enforceTaskPermission(func);
+    @Override
+    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+            throws RemoteException {
+        try {
+            return super.onTransact(code, data, reply, flags);
+        } catch (RuntimeException e) {
+            throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 09df71c..07610ab 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -226,9 +226,8 @@
         }
         int displayId = activity.getDisplayContent().getDisplayId();
         try {
-            final int res = session.addToDisplay(window, layoutParams,
-                    View.GONE, displayId, mTmpInsetsState, tmpFrames.frame,
-                    null /* outInputChannel */, mTmpInsetsState, mTempControls);
+            final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
+                    mTmpInsetsState, null /* outInputChannel */, mTmpInsetsState, mTempControls);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                 return null;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 46aea23..98eb11f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -372,8 +372,6 @@
                     dc.mWallpaperController.startWallpaperAnimation(anim);
                 }
             }
-        }
-        if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
             dc.startKeyguardExitOnNonAppWindows(
                     (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0,
                     (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 52ed278..1a0e16b 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -220,8 +220,6 @@
             mRemoveReplacedWindows = false;
         }
 
-        mService.destroyPreservedSurfaceLocked();
-
         mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         executeAfterPrepareSurfacesRunnables();
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index a3a9eb7..eed3299 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -26,9 +26,11 @@
 import android.os.IBinder;
 import android.view.Display;
 import android.view.IInputFilter;
+import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IWindow;
 import android.view.InputChannel;
 import android.view.MagnificationSpec;
+import android.view.RemoteAnimationTarget;
 import android.view.WindowInfo;
 import android.view.WindowManager.DisplayImePolicy;
 
@@ -154,6 +156,21 @@
     }
 
     /**
+     * An interface to be notified when keyguard exit animation should start.
+     */
+    public interface KeyguardExitAnimationStartListener {
+        /**
+         * Called when keyguard exit animation should start.
+         * @param apps The list of apps to animate.
+         * @param wallpapers The list of wallpapers to animate.
+         * @param finishedCallback The callback to invoke when the animation is finished.
+         */
+        void onAnimationStart(RemoteAnimationTarget[] apps,
+                RemoteAnimationTarget[] wallpapers,
+                IRemoteAnimationFinishedCallback finishedCallback);
+    }
+
+    /**
       * An interface to be notified about hardware keyboard status.
       */
     public interface OnHardKeyboardStatusChangeListener {
@@ -372,6 +389,14 @@
     public abstract void registerAppTransitionListener(AppTransitionListener listener);
 
     /**
+     * Registers a listener to be notified to start the keyguard exit animation.
+     *
+     * @param listener The listener to register.
+     */
+    public abstract void registerKeyguardExitAnimationStartListener(
+            KeyguardExitAnimationStartListener listener);
+
+    /**
      * Reports that the password for the given user has changed.
      */
     public abstract void reportPasswordChanged(int userId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 09fbce0..8965cab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -83,7 +83,6 @@
 import static android.view.WindowManager.TRANSIT_RELAUNCH;
 import static android.view.WindowManagerGlobal.ADD_OKAY;
 import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
-import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
@@ -405,6 +404,8 @@
     // trying to apply a new one.
     private static final boolean ALWAYS_KEEP_CURRENT = true;
 
+    static final int LOGTAG_INPUT_FOCUS = 62001;
+
     /**
      * Restrict ability of activities overriding transition animation in a way such that
      * an activity can do it only when the transition happens within a same task.
@@ -413,7 +414,6 @@
      */
     private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
             "persist.wm.disable_custom_task_animation";
-    static final int LOGTAG_INPUT_FOCUS = 62001;
 
     /**
      * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
@@ -421,6 +421,19 @@
     static boolean sDisableCustomTaskAnimationProperty =
             SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
 
+    /**
+     * Run Keyguard animation as remote animation in System UI instead of local animation in
+     * the server process.
+     */
+    private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+            "persist.wm.enable_remote_keyguard_animation";
+
+    /**
+     * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+     */
+    public static boolean sEnableRemoteKeyguardAnimation =
+            SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
     private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
             "ro.sf.disable_triple_buffer";
 
@@ -626,13 +639,6 @@
     final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
 
     /**
-     * Windows with a preserved surface waiting to be destroyed. These windows
-     * are going through a surface change. We keep the old surface around until
-     * the first frame on the new surface finishes drawing.
-     */
-    final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
-
-    /**
      * This is set when we have run out of memory, and will either be an empty
      * list or contain windows that need to be force removed.
      */
@@ -770,7 +776,8 @@
     final AnrController mAnrController;
 
     private final ScreenshotHashController mScreenshotHashController;
-    private final WindowContextListenerController mWindowContextListenerController =
+    @VisibleForTesting
+    final WindowContextListenerController mWindowContextListenerController =
             new WindowContextListenerController();
 
     @VisibleForTesting
@@ -1488,7 +1495,7 @@
     }
 
     public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
-            int displayId, int requestUserId, InsetsState requestedVisibility, Rect outFrame,
+            int displayId, int requestUserId, InsetsState requestedVisibility,
             InputChannel outInputChannel, InsetsState outInsetsState,
             InsetsSourceControl[] outActiveControls) {
         Arrays.fill(outActiveControls, null);
@@ -1830,7 +1837,7 @@
                 prepareNoneTransitionForRelaunching(activity);
             }
 
-            if (displayPolicy.getLayoutHint(win.mAttrs, token, outFrame, outInsetsState,
+            if (displayPolicy.getLayoutHint(win.mAttrs, token, outInsetsState,
                     win.isClientLocal())) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
             }
@@ -2328,7 +2335,6 @@
 
             if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
                     + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
-            winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
             if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
                 winAnimator.mAlpha = attrs.alpha;
             }
@@ -2595,11 +2601,12 @@
         } else if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
             focusMayChange = true;
             win.mAnimatingExit = true;
-        } else if (win.isAnimating(TRANSITION | PARENTS)) {
+        } else if (win.mDisplayContent.okToAnimate() && win.isAnimating(TRANSITION | PARENTS)) {
             // Currently in a hide animation... turn this into
             // an exit.
             win.mAnimatingExit = true;
-        } else if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) {
+        } else if (win.mDisplayContent.okToAnimate()
+                && win.mDisplayContent.mWallpaperController.isWallpaperTarget(win)) {
             // If the wallpaper is currently behind this
             // window, we need to change both of them inside
             // of a transaction to avoid artifacts.
@@ -2793,6 +2800,17 @@
         return WindowManagerGlobal.ADD_OKAY;
     }
 
+    /**
+     * Registers a listener for a {@link android.app.WindowContext} to subscribe to configuration
+     * changes of a {@link DisplayArea}.
+     *
+     * @param clientToken the window context's token
+     * @param type Window type of the window context
+     * @param displayId The display associated with the window context
+     * @param options A bundle used to pass window-related options and choose the right DisplayArea
+     *
+     * @return {@code true} if the listener was registered successfully.
+     */
     @Override
     public boolean registerWindowContextListener(IBinder clientToken, int type, int displayId,
             Bundle options) {
@@ -2848,6 +2866,7 @@
         }
     }
 
+    /** Returns {@code true} if this binder is a registered window token. */
     @Override
     public boolean isWindowToken(IBinder binder) {
         synchronized (mGlobalLock) {
@@ -5490,14 +5509,6 @@
         }
     }
 
-    void destroyPreservedSurfaceLocked() {
-        for (int i = mDestroyPreservedSurface.size() - 1; i >= 0 ; i--) {
-            final WindowState w = mDestroyPreservedSurface.get(i);
-            w.mWinAnimator.destroyPreservedSurfaceLocked(w.getSyncTransaction());
-        }
-        mDestroyPreservedSurface.clear();
-    }
-
     // -------------------------------------------------------------
     // IWindowManager API
     // -------------------------------------------------------------
@@ -7749,6 +7760,15 @@
         }
 
         @Override
+        public void registerKeyguardExitAnimationStartListener(
+                KeyguardExitAnimationStartListener listener) {
+            synchronized (mGlobalLock) {
+                getDefaultDisplayContentLocked().mAppTransition
+                        .registerKeygaurdExitAnimationStartListener(listener);
+            }
+        }
+
+        @Override
         public void reportPasswordChanged(int userId) {
             mKeyguardDisableHandler.updateKeyguardEnabled(userId);
         }
@@ -8555,8 +8575,8 @@
                             + "could not be found!");
                 }
                 final WindowToken windowToken = dc.getWindowToken(attrs.token);
-                return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken,
-                        mTmpRect /* outFrame */, outInsetsState, fromLocal);
+                return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken, outInsetsState,
+                        fromLocal);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 1b81914..63a0832 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -40,6 +40,7 @@
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.Parcel;
 import android.os.RemoteException;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -112,6 +113,16 @@
     }
 
     @Override
+    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+            throws RemoteException {
+        try {
+            return super.onTransact(code, data, reply, flags);
+        } catch (RuntimeException e) {
+            throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e);
+        }
+    }
+
+    @Override
     public void applyTransaction(WindowContainerTransaction t) {
         enforceTaskPermission("applyTransaction()");
         if (t == null) {
@@ -405,11 +416,7 @@
                         new Configuration(container.getRequestedOverrideConfiguration());
                 c.setTo(change.getConfiguration(), configMask, windowMask);
                 container.onRequestedOverrideConfigurationChanged(c);
-                // TODO(b/145675353): remove the following once we could apply new bounds to the
-                // root pinned task together with its children.
             }
-            resizeRootPinnedTaskIfNeeded(container, configMask, windowMask,
-                    container.getRequestedOverrideConfiguration());
             effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
         }
         if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
@@ -659,19 +666,6 @@
         return effects;
     }
 
-    private void resizeRootPinnedTaskIfNeeded(ConfigurationContainer container, int configMask,
-            int windowMask, Configuration config) {
-        if ((container instanceof Task)
-                && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
-                && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
-            final Task rootTask = (Task) container;
-            if (rootTask.inPinnedWindowingMode()) {
-                rootTask.resize(config.windowConfiguration.getBounds(),
-                        PRESERVE_WINDOWS, true /* deferResume */);
-            }
-        }
-    }
-
     @Override
     public ITaskOrganizerController getTaskOrganizerController() {
         enforceTaskPermission("getTaskOrganizerController()");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c698591..89cb163 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -45,6 +45,7 @@
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
@@ -103,7 +104,6 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
-import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -2242,7 +2242,6 @@
 
         disposeInputChannel();
 
-        mWinAnimator.destroyDeferredSurfaceLocked(mTmpTransaction);
         mWinAnimator.destroySurfaceLocked(mTmpTransaction);
         mTmpTransaction.apply();
         mSession.windowRemovedLocked();
@@ -3308,11 +3307,6 @@
             return destroyedSomething;
         }
 
-        if (appStopped || mWindowRemovalAllowed) {
-            mWinAnimator.destroyPreservedSurfaceLocked(mTmpTransaction);
-            mTmpTransaction.apply();
-        }
-
         if (mDestroying) {
             ProtoLog.e(WM_DEBUG_ADD_REMOVE, "win=%s"
                     + " destroySurfaces: appStopped=%b"
@@ -5000,23 +4994,8 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
-        // When we change the Surface size, in scenarios which may require changing
-        // the surface position in sync with the resize, we use a preserved surface
-        // so we can freeze it while waiting for the client to report draw on the newly
-        // sized surface. At the moment this logic is only in place for switching
-        // in and out of the big surface for split screen resize.
         if (isDragResizeChanged()) {
             setDragResizing();
-            // We can only change top level windows to the full-screen surface when
-            // resizing (as we only have one full-screen surface). So there is no need
-            // to preserve and destroy windows which are attached to another, they
-            // will keep their surface and its size may change over time.
-            if (mHasSurface && !isChildWindow()) {
-                mWinAnimator.preserveSurfaceLocked(getSyncTransaction());
-                result |= RELAYOUT_RES_SURFACE_CHANGED |
-                    RELAYOUT_RES_FIRST_TIME;
-                scheduleAnimation();
-            }
         }
         final boolean freeformResizing = isDragResizing()
                 && getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
@@ -5269,7 +5248,7 @@
         if (!mAnimatingExit && mAppDied) {
             mIsDimming = true;
             getDimmer().dimAbove(getSyncTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
-        } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || mAttrs.backgroundBlurRadius != 0)
+        } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || (mAttrs.flags & FLAG_BLUR_BEHIND) != 0)
                    && isVisibleNow() && !mHidden) {
             // Only show the Dimmer when the following is satisfied:
             // 1. The window has the flag FLAG_DIM_BEHIND or background blur is requested
@@ -5278,8 +5257,10 @@
             // 4. The WS is not hidden.
             mIsDimming = true;
             final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0;
+            final int blurRadius =
+                    (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 ? mAttrs.blurBehindRadius : 0;
             getDimmer().dimBelow(
-                    getSyncTransaction(), this, mAttrs.dimAmount, mAttrs.backgroundBlurRadius);
+                    getSyncTransaction(), this, mAttrs.dimAmount, mAttrs.blurBehindRadius);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c8b940a..d164f30 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -116,15 +116,7 @@
     boolean mAnimationIsEntrance;
 
     WindowSurfaceController mSurfaceController;
-    private WindowSurfaceController mPendingDestroySurface;
 
-    /**
-     * Set if the client has asked that the destroy of its surface be delayed
-     * until it explicitly says it is okay.
-     */
-    boolean mSurfaceDestroyDeferred;
-
-    private boolean mDestroyPreservedSurfaceUponRedraw;
     float mShownAlpha = 0;
     float mAlpha = 0;
     float mLastAlpha = 0;
@@ -257,11 +249,6 @@
             //dump();
             mLastHidden = true;
 
-            // We may have a preserved surface which we no longer need. If there was a quick
-            // VISIBLE, GONE, VISIBLE, GONE sequence, the surface may never draw, so we don't mark
-            // it to be destroyed in prepareSurfaceLocked.
-            markPreservedSurfaceForDestroy();
-
             if (mSurfaceController != null) {
                 mSurfaceController.hide(transaction, reason);
             }
@@ -323,70 +310,6 @@
         return result;
     }
 
-    void preserveSurfaceLocked(SurfaceControl.Transaction t) {
-        if (mDestroyPreservedSurfaceUponRedraw) {
-            // This could happen when switching the surface mode very fast. For example,
-            // we preserved a surface when dragResizing changed to true. Then before the
-            // preserved surface is removed, dragResizing changed to false again.
-            // In this case, we need to leave the preserved surface alone, and destroy
-            // the actual surface, so that the createSurface call could create a surface
-            // of the proper size. The preserved surface will still be removed when client
-            // finishes drawing to the new surface.
-            mSurfaceDestroyDeferred = false;
-
-            // Make sure to reparent any children of the new surface back to the preserved
-            // surface before destroying it.
-            if (mSurfaceController != null && mPendingDestroySurface != null) {
-                mPostDrawTransaction.reparentChildren(
-                    mSurfaceController.mSurfaceControl,
-                    mPendingDestroySurface.mSurfaceControl).apply();
-            }
-            destroySurfaceLocked(t);
-            mSurfaceDestroyDeferred = true;
-            return;
-        }
-        ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SET FREEZE LAYER: %s", mWin);
-        if (mSurfaceController != null) {
-            // Our SurfaceControl is always at layer 0 within the parent Surface managed by
-            // window-state. We want this old Surface to stay on top of the new one
-            // until we do the swap, so we place it at a positive layer.
-            t.setLayer(mSurfaceController.mSurfaceControl, PRESERVED_SURFACE_LAYER);
-        }
-        mDestroyPreservedSurfaceUponRedraw = true;
-        mSurfaceDestroyDeferred = true;
-        destroySurfaceLocked(t);
-    }
-
-    void destroyPreservedSurfaceLocked(SurfaceControl.Transaction t) {
-        if (!mDestroyPreservedSurfaceUponRedraw) {
-            return;
-        }
-
-        // If we are preserving a surface but we aren't relaunching that means
-        // we are just doing an in-place switch. In that case any SurfaceFlinger side
-        // child layers need to be reparented to the new surface to make this
-        // transparent to the app.
-        // If the children are detached, we don't want to reparent them to the new surface.
-        // Instead let the children get removed when the old surface is deleted.
-        if (mSurfaceController != null && mPendingDestroySurface != null
-                && !mPendingDestroySurface.mChildrenDetached
-                && (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) {
-            mPostDrawTransaction.reparentChildren(
-                    mPendingDestroySurface.mSurfaceControl,
-                    mSurfaceController.mSurfaceControl).apply();
-        }
-
-        destroyDeferredSurfaceLocked(t);
-        mDestroyPreservedSurfaceUponRedraw = false;
-    }
-
-    private void markPreservedSurfaceForDestroy() {
-        if (mDestroyPreservedSurfaceUponRedraw
-                && !mService.mDestroyPreservedSurface.contains(mWin)) {
-            mService.mDestroyPreservedSurface.add(mWin);
-        }
-    }
-
     void resetDrawState() {
         mDrawState = DRAW_PENDING;
 
@@ -508,39 +431,23 @@
             return;
         }
 
-        // When destroying a surface we want to make sure child windows are hidden. If we are
-        // preserving the surface until redraw though we intend to swap it out with another surface
-        // for resizing. In this case the window always remains visible to the user and the child
-        // windows should likewise remain visible.
-        if (!mDestroyPreservedSurfaceUponRedraw) {
-            mWin.mHidden = true;
-        }
+        mWin.mHidden = true;
 
         try {
-            if (DEBUG_VISIBILITY) logWithStack(TAG, "Window " + this + " destroying surface "
-                    + mSurfaceController + ", session " + mSession);
-            if (mSurfaceDestroyDeferred) {
-                if (mSurfaceController != null && mPendingDestroySurface != mSurfaceController) {
-                    if (mPendingDestroySurface != null) {
-                        ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
-                                mWin, new RuntimeException().fillInStackTrace());
-                        mPendingDestroySurface.destroy(t);
-                    }
-                    mPendingDestroySurface = mSurfaceController;
-                }
-            } else {
-                ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
-                        mWin, new RuntimeException().fillInStackTrace());
-                destroySurface(t);
+            if (DEBUG_VISIBILITY) {
+                logWithStack(TAG, "Window " + this + " destroying surface "
+                        + mSurfaceController + ", session " + mSession);
             }
+            ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
+                    mWin, new RuntimeException().fillInStackTrace());
+            destroySurface(t);
             // Don't hide wallpaper if we're deferring the surface destroy
             // because of a surface change.
-            if (!mDestroyPreservedSurfaceUponRedraw) {
-                mWallpaperControllerLocked.hideWallpapers(mWin);
-            }
+            mWallpaperControllerLocked.hideWallpapers(mWin);
         } catch (RuntimeException e) {
             Slog.w(TAG, "Exception thrown when destroying Window " + this
-                + " surface " + mSurfaceController + " session " + mSession + ": " + e.toString());
+                    + " surface " + mSurfaceController + " session " + mSession + ": "
+                    + e.toString());
         }
 
         // Whether the surface was preserved (and copied to mPendingDestroySurface) or not, it
@@ -554,27 +461,6 @@
         mDrawState = NO_SURFACE;
     }
 
-    void destroyDeferredSurfaceLocked(SurfaceControl.Transaction t) {
-        try {
-            if (mPendingDestroySurface != null) {
-                ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
-                        mWin, new RuntimeException().fillInStackTrace());
-                mPendingDestroySurface.destroy(t);
-                // Don't hide wallpaper if we're destroying a deferred surface
-                // after a surface mode change.
-                if (!mDestroyPreservedSurfaceUponRedraw) {
-                    mWallpaperControllerLocked.hideWallpapers(mWin);
-                }
-            }
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Exception thrown when destroying Window "
-                    + this + " surface " + mPendingDestroySurface
-                    + " session " + mSession + ": " + e.toString());
-        }
-        mSurfaceDestroyDeferred = false;
-        mPendingDestroySurface = null;
-    }
-
     void computeShownFrameLocked() {
         if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
             return;
@@ -744,7 +630,6 @@
             if (prepared && mDrawState == HAS_DRAWN) {
                 if (mLastHidden) {
                     if (showSurfaceRobustlyLocked(t)) {
-                        markPreservedSurfaceForDestroy();
                         mAnimator.requestRemovalOfReplacedWindows(w);
                         mLastHidden = false;
                         if (mIsWallpaper) {
@@ -905,20 +790,6 @@
         if (!shown)
             return false;
 
-        // If we had a preserved surface it's no longer needed, and it may be harmful
-        // if we are transparent.
-        if (mPendingDestroySurface != null && mDestroyPreservedSurfaceUponRedraw) {
-            final SurfaceControl pendingSurfaceControl = mPendingDestroySurface.mSurfaceControl;
-            mPostDrawTransaction.reparent(pendingSurfaceControl, null);
-            // If the children are detached, we don't want to reparent them to the new surface.
-            // Instead let the children get removed when the old surface is deleted.
-            if (!mPendingDestroySurface.mChildrenDetached) {
-                mPostDrawTransaction.reparentChildren(
-                        mPendingDestroySurface.mSurfaceControl,
-                        mSurfaceController.mSurfaceControl);
-            }
-        }
-
         t.merge(mPostDrawTransaction);
         return true;
     }
@@ -1058,13 +929,6 @@
             pw.println();
         }
 
-        if (mPendingDestroySurface != null) {
-            pw.print(prefix); pw.print("mPendingDestroySurface=");
-                    pw.println(mPendingDestroySurface);
-        }
-        if (mSurfaceDestroyDeferred) {
-                    pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
-        }
         if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
             pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
                     pw.print(" mAlpha="); pw.print(mAlpha);
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 5f4477d..82ba3c1 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -55,8 +55,6 @@
     private boolean mSurfaceShown = false;
     private float mSurfaceX = 0;
     private float mSurfaceY = 0;
-    private int mSurfaceW = 0;
-    private int mSurfaceH = 0;
 
     // Initialize to the identity matrix.
     private float mLastDsdx = 1;
@@ -82,9 +80,6 @@
             int flags, WindowStateAnimator animator, int windowType) {
         mAnimator = animator;
 
-        mSurfaceW = w;
-        mSurfaceH = h;
-
         title = name;
 
         mService = animator.mService;
@@ -104,8 +99,8 @@
                 .setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
                 .setCallsite("WindowSurfaceController");
 
-        final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags &
-                WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
+        final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags
+                & WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
 
         if (useBLAST) {
             b.setBLASTLayer();
@@ -119,7 +114,6 @@
     void hide(SurfaceControl.Transaction transaction, String reason) {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title);
 
-        mAnimator.destroyPreservedSurfaceLocked(transaction);
         if (mSurfaceShown) {
             hideSurface(transaction);
         }
@@ -335,9 +329,7 @@
         pw.print(" layer="); pw.print(mSurfaceLayer);
         pw.print(" alpha="); pw.print(mSurfaceAlpha);
         pw.print(" rect=("); pw.print(mSurfaceX);
-        pw.print(","); pw.print(mSurfaceY);
-        pw.print(") "); pw.print(mSurfaceW);
-        pw.print(" x "); pw.print(mSurfaceH);
+        pw.print(","); pw.print(mSurfaceY); pw.print(") ");
         pw.print(" transform=("); pw.print(mLastDsdx); pw.print(", ");
         pw.print(mLastDtdx); pw.print(", "); pw.print(mLastDsdy);
         pw.print(", "); pw.print(mLastDtdy); pw.println(")");
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 8a512bc..cd18311 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static android.os.Process.INVALID_UID;
-import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -26,7 +25,6 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
-import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -41,7 +39,6 @@
 
 import android.annotation.CallSuper;
 import android.annotation.Nullable;
-import android.app.IWindowToken;
 import android.app.servertransaction.FixedRotationAdjustmentsItem;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -58,7 +55,6 @@
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
 
@@ -112,17 +108,10 @@
 
     private FixedRotationTransformState mFixedRotationTransformState;
 
-    private Configuration mLastReportedConfig;
-    private int mLastReportedDisplay = INVALID_DISPLAY;
-
     /**
      * When set to {@code true}, this window token is created from {@link android.app.WindowContext}
      */
-    @VisibleForTesting
-    final boolean mFromClientToken;
-
-    private DeathRecipient mDeathRecipient;
-    private boolean mBinderDied = false;
+    private final boolean mFromClientToken;
 
     private final int mOwnerUid;
 
@@ -188,30 +177,6 @@
         }
     }
 
-    private class DeathRecipient implements IBinder.DeathRecipient {
-        private boolean mHasUnlinkToDeath = false;
-
-        @Override
-        public void binderDied() {
-            synchronized (mWmService.mGlobalLock) {
-                mBinderDied = true;
-                removeImmediately();
-            }
-        }
-
-        void linkToDeath() throws RemoteException {
-            token.linkToDeath(DeathRecipient.this, 0);
-        }
-
-        void unlinkToDeath() {
-            if (mHasUnlinkToDeath) {
-                return;
-            }
-            token.unlinkToDeath(DeathRecipient.this, 0);
-            mHasUnlinkToDeath = true;
-        }
-    }
-
     /**
      * Compares two child window of this token and returns -1 if the first is lesser than the
      * second in terms of z-order and 1 otherwise.
@@ -266,17 +231,6 @@
         if (dc != null) {
             dc.addWindowToken(token, this);
         }
-        if (shouldReportToClient()) {
-            try {
-                mDeathRecipient = new DeathRecipient();
-                mDeathRecipient.linkToDeath();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to add window token with type " + windowType + " on "
-                        + "display " + dc.getDisplayId(), e);
-                mDeathRecipient = null;
-                return;
-            }
-        }
     }
 
     void removeAllWindowsIfPossible() {
@@ -414,22 +368,6 @@
         // Needs to occur after the token is removed from the display above to avoid attempt at
         // duplicate removal of this window container from it's parent.
         super.removeImmediately();
-
-        reportWindowTokenRemovedToClient();
-    }
-
-    // TODO(b/159767464): Remove after we migrate to listener approach.
-    private void reportWindowTokenRemovedToClient() {
-        if (!shouldReportToClient()) {
-            return;
-        }
-        mDeathRecipient.unlinkToDeath();
-        IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
-        try {
-            windowTokenClient.onWindowTokenRemoved();
-        } catch (RemoteException e) {
-            ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
-        }
     }
 
     @Override
@@ -441,51 +379,11 @@
         // to another display before the window behind
         // it is ready.
         super.onDisplayChanged(dc);
-        reportConfigToWindowTokenClient();
     }
 
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         super.onConfigurationChanged(newParentConfig);
-        reportConfigToWindowTokenClient();
-    }
-
-    void reportConfigToWindowTokenClient() {
-        if (!shouldReportToClient()) {
-            return;
-        }
-        if (mLastReportedConfig == null) {
-            mLastReportedConfig = new Configuration();
-        }
-        final Configuration config = getConfiguration();
-        final int displayId = getDisplayContent().getDisplayId();
-        if (config.diff(mLastReportedConfig) == 0 && displayId == mLastReportedDisplay) {
-            // No changes since last reported time.
-            return;
-        }
-
-        mLastReportedConfig.setTo(config);
-        mLastReportedDisplay = displayId;
-
-        IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
-        try {
-            windowTokenClient.onConfigurationChanged(config, displayId);
-        } catch (RemoteException e) {
-            ProtoLog.w(WM_ERROR,
-                    "Could not report config changes to the window token client.");
-        }
-    }
-
-    /**
-     * @return {@code true} if this {@link WindowToken} is not an {@link ActivityRecord} and
-     * registered from client side.
-     */
-    private boolean shouldReportToClient() {
-        // Only report to client for WindowToken because Activities are updated through ATM
-        // callbacks.
-        return asActivityRecord() == null
-        // Report to {@link android.view.WindowTokenClient} if this token was registered from it.
-                && mFromClientToken && !mBinderDied;
     }
 
     @Override
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 995cfe9..9a8942b 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -12,6 +12,9 @@
 per-file com_android_server_HardwarePropertiesManagerService.cpp = michaelwr@google.com, santoscordon@google.com
 per-file com_android_server_power_PowerManagerService.* = michaelwr@google.com, santoscordon@google.com
 
+# BatteryStats
+per-file com_android_server_am_BatteryStatsService.cpp = file:/BATTERY_STATS_OWNERS
+
 per-file Android.bp = file:platform/build/soong:/OWNERS
 per-file com_android_server_Usb* = file:/services/usb/OWNERS
 per-file com_android_server_Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index c3e7c7a..16eaa77 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -55,15 +55,8 @@
 using android::hardware::Return;
 using android::hardware::Void;
 using android::hardware::power::stats::V1_0::IPowerStats;
-using android::hardware::power::V1_0::PowerStatePlatformSleepState;
-using android::hardware::power::V1_0::PowerStateVoter;
-using android::hardware::power::V1_0::Status;
-using android::hardware::power::V1_1::PowerStateSubsystem;
-using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
 using android::system::suspend::BnSuspendCallback;
 using android::system::suspend::ISuspendControlService;
-using IPowerV1_1 = android::hardware::power::V1_1::IPower;
-using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
 namespace android
 {
@@ -74,23 +67,9 @@
 static sem_t wakeup_sem;
 extern sp<ISuspendControlService> getSuspendControl();
 
-// Java methods used in getLowPowerStats
-static jmethodID jgetAndUpdatePlatformState = NULL;
-static jmethodID jgetSubsystem = NULL;
-static jmethodID jputVoter = NULL;
-static jmethodID jputState = NULL;
-
 std::mutex gPowerStatsHalMutex;
-std::unordered_map<uint32_t, std::string> gPowerStatsHalEntityNames = {};
-std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>>
-    gPowerStatsHalStateNames = {};
-std::vector<uint32_t> gPowerStatsHalPlatformIds = {};
-std::vector<uint32_t> gPowerStatsHalSubsystemIds = {};
 sp<IPowerStats> gPowerStatsHalV1_0 = nullptr;
 
-std::function<void(JNIEnv*, jobject)> gGetLowPowerStatsImpl = {};
-std::function<jint(JNIEnv*, jobject)> gGetPlatformLowPowerStatsImpl = {};
-std::function<jint(JNIEnv*, jobject)> gGetSubsystemLowPowerStatsImpl = {};
 std::function<void(JNIEnv*, jobject)> gGetRailEnergyPowerStatsImpl = {};
 
 // Cellular/Wifi power monitor rail information
@@ -222,68 +201,14 @@
     return true;
 }
 
-static bool checkPowerHalResult(const Return<void>& ret, const char* function) {
-    if (!ret.isOk()) {
-        ALOGE("%s failed: requested HAL service not available.", function);
-        power::PowerHalLoader::unloadAll();
-        return false;
-    }
-    return true;
-}
-
 // gPowerStatsHalV1_0 must not be null
 static bool initializePowerStatsLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
     using android::hardware::power::stats::V1_0::Status;
-    using android::hardware::power::stats::V1_0::PowerEntityType;
 
     // Clear out previous content if we are re-initializing
-    gPowerStatsHalEntityNames.clear();
-    gPowerStatsHalStateNames.clear();
-    gPowerStatsHalPlatformIds.clear();
-    gPowerStatsHalSubsystemIds.clear();
     gPowerStatsHalRailNames.clear();
 
     Return<void> ret;
-    ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
-        if (status != Status::SUCCESS) {
-            ALOGE("Error getting power entity info");
-            return;
-        }
-
-        // construct lookup table of powerEntityId to power entity name
-        // also construct vector of platform and subsystem IDs
-        for (auto info : infos) {
-            gPowerStatsHalEntityNames.emplace(info.powerEntityId, info.powerEntityName);
-            if (info.type == PowerEntityType::POWER_DOMAIN) {
-                gPowerStatsHalPlatformIds.emplace_back(info.powerEntityId);
-            } else {
-                gPowerStatsHalSubsystemIds.emplace_back(info.powerEntityId);
-            }
-        }
-    });
-    if (!checkPowerStatsHalResultLocked(ret, __func__)) {
-        return false;
-    }
-
-    ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
-        if (status != Status::SUCCESS) {
-            ALOGE("Error getting state info");
-            return;
-        }
-
-        // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
-        for (auto stateSpace : stateSpaces) {
-            std::unordered_map<uint32_t, std::string> stateNames = {};
-            for (auto state : stateSpace.states) {
-                stateNames.emplace(state.powerEntityStateId,
-                    state.powerEntityStateName);
-            }
-            gPowerStatsHalStateNames.emplace(stateSpace.powerEntityId, stateNames);
-        }
-    });
-    if (!checkPowerStatsHalResultLocked(ret, __func__)) {
-        return false;
-    }
 
     // Get Power monitor rails available
     ret = gPowerStatsHalV1_0->getRailInfo([](auto rails, auto status) {
@@ -306,7 +231,7 @@
         return false;
     }
 
-    return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty());
+    return true;
 }
 
 static bool getPowerStatsHalLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
@@ -333,196 +258,6 @@
     return true;
 }
 
-static void getPowerStatsHalLowPowerDataLocked(JNIEnv* env, jobject jrpmStats)
-        EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
-    using android::hardware::power::stats::V1_0::Status;
-
-    if (!getPowerStatsHalLocked()) {
-        ALOGE("failed to get low power stats");
-        return;
-    }
-
-    // Get power entity state residency data
-    bool success = false;
-    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData({},
-        [&env, &jrpmStats, &success](auto results, auto status) {
-            if (status == Status::NOT_SUPPORTED) {
-                ALOGW("getPowerEntityStateResidencyData is not supported");
-                success = false;
-                return;
-            }
-
-            for (auto result : results) {
-                jobject jsubsystem = env->CallObjectMethod(jrpmStats, jgetSubsystem,
-                    env->NewStringUTF(gPowerStatsHalEntityNames.at(result.powerEntityId).c_str()));
-                if (jsubsystem == NULL) {
-                    ALOGE("The rpmstats jni jobject jsubsystem is null.");
-                    return;
-                }
-                for (auto stateResidency : result.stateResidencyData) {
-
-                    env->CallVoidMethod(jsubsystem, jputState,
-                        env->NewStringUTF(gPowerStatsHalStateNames.at(result.powerEntityId)
-                        .at(stateResidency.powerEntityStateId).c_str()),
-                        stateResidency.totalTimeInStateMs,
-                        stateResidency.totalStateEntryCount);
-                }
-            }
-            success = true;
-        });
-    checkPowerStatsHalResultLocked(ret, __func__);
-    if (!success) {
-        ALOGE("getPowerEntityStateResidencyData failed");
-    }
-}
-
-static jint getPowerStatsHalPlatformDataLocked(JNIEnv* env, jobject outBuf)
-        EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
-    using android::hardware::power::stats::V1_0::Status;
-    using hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
-    using hardware::power::stats::V1_0::PowerEntityStateResidencyData;
-
-    if (!getPowerStatsHalLocked()) {
-        ALOGE("failed to get low power stats");
-        return -1;
-    }
-
-    char *output = (char*)env->GetDirectBufferAddress(outBuf);
-    char *offset = output;
-    int remaining = (int)env->GetDirectBufferCapacity(outBuf);
-    int total_added = -1;
-
-    // Get power entity state residency data
-    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
-        gPowerStatsHalPlatformIds,
-        [&offset, &remaining, &total_added](auto results, auto status) {
-            if (status == Status::NOT_SUPPORTED) {
-                ALOGW("getPowerEntityStateResidencyData is not supported");
-                return;
-            }
-
-            for (size_t i = 0; i < results.size(); i++) {
-                const PowerEntityStateResidencyResult& result = results[i];
-
-                for (size_t j = 0; j < result.stateResidencyData.size(); j++) {
-                    const PowerEntityStateResidencyData& stateResidency =
-                        result.stateResidencyData[j];
-                    int added = snprintf(offset, remaining,
-                        "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
-                        j + 1, gPowerStatsHalStateNames.at(result.powerEntityId)
-                           .at(stateResidency.powerEntityStateId).c_str(),
-                        stateResidency.totalTimeInStateMs,
-                        stateResidency.totalStateEntryCount);
-                    if (added < 0) {
-                        break;
-                    }
-                    if (added > remaining) {
-                        added = remaining;
-                    }
-                    offset += added;
-                    remaining -= added;
-                    total_added += added;
-                }
-                if (remaining <= 0) {
-                    /* rewrite NULL character*/
-                    offset--;
-                    total_added--;
-                    ALOGE("power.stats Hal: buffer not enough");
-                    break;
-                }
-            }
-        });
-    if (!checkPowerStatsHalResultLocked(ret, __func__)) {
-        return -1;
-    }
-
-    total_added += 1;
-    return total_added;
-}
-
-static jint getPowerStatsHalSubsystemDataLocked(JNIEnv* env, jobject outBuf)
-        EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
-    using android::hardware::power::stats::V1_0::Status;
-    using hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
-    using hardware::power::stats::V1_0::PowerEntityStateResidencyData;
-
-    if (!getPowerStatsHalLocked()) {
-        ALOGE("failed to get low power stats");
-        return -1;
-    }
-
-    char *output = (char*)env->GetDirectBufferAddress(outBuf);
-    char *offset = output;
-    int remaining = (int)env->GetDirectBufferCapacity(outBuf);
-    int total_added = -1;
-
-    // Get power entity state residency data
-    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
-        gPowerStatsHalSubsystemIds,
-        [&offset, &remaining, &total_added](auto results, auto status) {
-            if (status == Status::NOT_SUPPORTED) {
-                ALOGW("getPowerEntityStateResidencyData is not supported");
-                return;
-            }
-
-            int added = snprintf(offset, remaining, "SubsystemPowerState ");
-            offset += added;
-            remaining -= added;
-            total_added += added;
-
-            for (size_t i = 0; i < results.size(); i++) {
-                const PowerEntityStateResidencyResult& result = results[i];
-                added = snprintf(offset, remaining, "subsystem_%zu name=%s ",
-                        i + 1, gPowerStatsHalEntityNames.at(result.powerEntityId).c_str());
-                if (added < 0) {
-                    break;
-                }
-
-                if (added > remaining) {
-                    added = remaining;
-                }
-
-                offset += added;
-                remaining -= added;
-                total_added += added;
-
-                for (size_t j = 0; j < result.stateResidencyData.size(); j++) {
-                    const PowerEntityStateResidencyData& stateResidency =
-                        result.stateResidencyData[j];
-                    added = snprintf(offset, remaining,
-                        "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry=%"
-                        PRIu64 " ", j + 1, gPowerStatsHalStateNames.at(result.powerEntityId)
-                           .at(stateResidency.powerEntityStateId).c_str(),
-                        stateResidency.totalTimeInStateMs,
-                        stateResidency.totalStateEntryCount,
-                        stateResidency.lastEntryTimestampMs);
-                    if (added < 0) {
-                        break;
-                    }
-                    if (added > remaining) {
-                        added = remaining;
-                    }
-                    offset += added;
-                    remaining -= added;
-                    total_added += added;
-                }
-                if (remaining <= 0) {
-                    /* rewrite NULL character*/
-                    offset--;
-                    total_added--;
-                    ALOGE("power.stats Hal: buffer not enough");
-                    break;
-                }
-            }
-        });
-    if (!checkPowerStatsHalResultLocked(ret, __func__)) {
-        return -1;
-    }
-
-    total_added += 1;
-    return total_added;
-}
-
 static void getPowerStatsHalRailEnergyDataLocked(JNIEnv* env, jobject jrailStats)
         EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
     using android::hardware::power::stats::V1_0::Status;
@@ -568,325 +303,17 @@
     }
 }
 
-static void getPowerHalLowPowerData(JNIEnv* env, jobject jrpmStats) {
-    sp<IPowerV1_0> powerHalV1_0 = power::PowerHalLoader::loadHidlV1_0();
-    if (powerHalV1_0 == nullptr) {
-        ALOGE("Power Hal not loaded");
-        return;
-    }
-
-    Return<void> ret = powerHalV1_0->getPlatformLowPowerStats(
-            [&env, &jrpmStats](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
-
-            if (status != Status::SUCCESS) return;
-
-            for (size_t i = 0; i < states.size(); i++) {
-                const PowerStatePlatformSleepState& state = states[i];
-
-                jobject jplatformState = env->CallObjectMethod(jrpmStats,
-                        jgetAndUpdatePlatformState,
-                        env->NewStringUTF(state.name.c_str()),
-                        state.residencyInMsecSinceBoot,
-                        state.totalTransitions);
-                if (jplatformState == NULL) {
-                    ALOGE("The rpmstats jni jobject jplatformState is null.");
-                    return;
-                }
-
-                for (size_t j = 0; j < state.voters.size(); j++) {
-                    const PowerStateVoter& voter = state.voters[j];
-                    env->CallVoidMethod(jplatformState, jputVoter,
-                            env->NewStringUTF(voter.name.c_str()),
-                            voter.totalTimeInMsecVotedForSinceBoot,
-                            voter.totalNumberOfTimesVotedSinceBoot);
-                }
-            }
-    });
-    if (!checkPowerHalResult(ret, "getPlatformLowPowerStats")) {
-        return;
-    }
-
-    // Trying to get IPower 1.1, this will succeed only for devices supporting 1.1
-    sp<IPowerV1_1> powerHal_1_1 = power::PowerHalLoader::loadHidlV1_1();
-    if (powerHal_1_1 == nullptr) {
-        // This device does not support IPower@1.1, exiting gracefully
-        return;
-    }
-    ret = powerHal_1_1->getSubsystemLowPowerStats(
-            [&env, &jrpmStats](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
-
-        if (status != Status::SUCCESS) return;
-
-        if (subsystems.size() > 0) {
-            for (size_t i = 0; i < subsystems.size(); i++) {
-                const PowerStateSubsystem &subsystem = subsystems[i];
-
-                jobject jsubsystem = env->CallObjectMethod(jrpmStats, jgetSubsystem,
-                        env->NewStringUTF(subsystem.name.c_str()));
-                if (jsubsystem == NULL) {
-                    ALOGE("The rpmstats jni jobject jsubsystem is null.");
-                    return;
-                }
-
-                for (size_t j = 0; j < subsystem.states.size(); j++) {
-                    const PowerStateSubsystemSleepState& state = subsystem.states[j];
-                    env->CallVoidMethod(jsubsystem, jputState,
-                            env->NewStringUTF(state.name.c_str()),
-                            state.residencyInMsecSinceBoot,
-                            state.totalTransitions);
-                }
-            }
-        }
-    });
-    checkPowerHalResult(ret, "getSubsystemLowPowerStats");
-}
-
-static jint getPowerHalPlatformData(JNIEnv* env, jobject outBuf) {
-    char *output = (char*)env->GetDirectBufferAddress(outBuf);
-    char *offset = output;
-    int remaining = (int)env->GetDirectBufferCapacity(outBuf);
-    int total_added = -1;
-
-    {
-        sp<IPowerV1_0> powerHalV1_0 = power::PowerHalLoader::loadHidlV1_0();
-        if (powerHalV1_0 == nullptr) {
-            ALOGE("Power Hal not loaded");
-            return -1;
-        }
-
-        Return<void> ret = powerHalV1_0->getPlatformLowPowerStats(
-            [&offset, &remaining, &total_added](hidl_vec<PowerStatePlatformSleepState> states,
-                    Status status) {
-                if (status != Status::SUCCESS)
-                    return;
-                for (size_t i = 0; i < states.size(); i++) {
-                    int added;
-                    const PowerStatePlatformSleepState& state = states[i];
-
-                    added = snprintf(offset, remaining,
-                        "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
-                        i + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
-                        state.totalTransitions);
-                    if (added < 0) {
-                        break;
-                    }
-                    if (added > remaining) {
-                        added = remaining;
-                    }
-                    offset += added;
-                    remaining -= added;
-                    total_added += added;
-
-                    for (size_t j = 0; j < state.voters.size(); j++) {
-                        const PowerStateVoter& voter = state.voters[j];
-                        added = snprintf(offset, remaining,
-                                "voter_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
-                                j + 1, voter.name.c_str(),
-                                voter.totalTimeInMsecVotedForSinceBoot,
-                                voter.totalNumberOfTimesVotedSinceBoot);
-                        if (added < 0) {
-                            break;
-                        }
-                        if (added > remaining) {
-                            added = remaining;
-                        }
-                        offset += added;
-                        remaining -= added;
-                        total_added += added;
-                    }
-
-                    if (remaining <= 0) {
-                        /* rewrite NULL character*/
-                        offset--;
-                        total_added--;
-                        ALOGE("PowerHal: buffer not enough");
-                        break;
-                    }
-                }
-            }
-        );
-
-        if (!checkPowerHalResult(ret, "getPlatformLowPowerStats")) {
-            return -1;
-        }
-    }
-    *offset = 0;
-    total_added += 1;
-    return total_added;
-}
-
-static jint getPowerHalSubsystemData(JNIEnv* env, jobject outBuf) {
-    char *output = (char*)env->GetDirectBufferAddress(outBuf);
-    char *offset = output;
-    int remaining = (int)env->GetDirectBufferCapacity(outBuf);
-    int total_added = -1;
-
-    // This is a IPower 1.1 API
-    sp<IPowerV1_1> powerHal_1_1 = nullptr;
-
-    {
-        // Trying to get 1.1, this will succeed only for devices supporting 1.1
-        powerHal_1_1 = power::PowerHalLoader::loadHidlV1_1();
-        if (powerHal_1_1 == nullptr) {
-            //This device does not support IPower@1.1, exiting gracefully
-            return 0;
-        }
-
-        Return<void> ret = powerHal_1_1->getSubsystemLowPowerStats(
-           [&offset, &remaining, &total_added](hidl_vec<PowerStateSubsystem> subsystems,
-                Status status) {
-
-            if (status != Status::SUCCESS)
-                return;
-
-            if (subsystems.size() > 0) {
-                int added = snprintf(offset, remaining, "SubsystemPowerState ");
-                offset += added;
-                remaining -= added;
-                total_added += added;
-
-                for (size_t i = 0; i < subsystems.size(); i++) {
-                    const PowerStateSubsystem &subsystem = subsystems[i];
-
-                    added = snprintf(offset, remaining,
-                                     "subsystem_%zu name=%s ", i + 1, subsystem.name.c_str());
-                    if (added < 0) {
-                        break;
-                    }
-
-                    if (added > remaining) {
-                        added = remaining;
-                    }
-
-                    offset += added;
-                    remaining -= added;
-                    total_added += added;
-
-                    for (size_t j = 0; j < subsystem.states.size(); j++) {
-                        const PowerStateSubsystemSleepState& state = subsystem.states[j];
-                        added = snprintf(offset, remaining,
-                                         "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry=%" PRIu64 " ",
-                                         j + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
-                                         state.totalTransitions, state.lastEntryTimestampMs);
-                        if (added < 0) {
-                            break;
-                        }
-
-                        if (added > remaining) {
-                            added = remaining;
-                        }
-
-                        offset += added;
-                        remaining -= added;
-                        total_added += added;
-                    }
-
-                    if (remaining <= 0) {
-                        /* rewrite NULL character*/
-                        offset--;
-                        total_added--;
-                        ALOGE("PowerHal: buffer not enough");
-                        break;
-                    }
-                }
-            }
-        }
-        );
-
-        if (!checkPowerHalResult(ret, "getSubsystemLowPowerStats")) {
-            return -1;
-        }
-    }
-
-    *offset = 0;
-    total_added += 1;
-    return total_added;
-}
-
 static void setUpPowerStatsLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
     // First see if power.stats HAL is available. Fall back to power HAL if
     // power.stats HAL is unavailable.
     if (IPowerStats::getService() != nullptr) {
         ALOGI("Using power.stats HAL");
-        gGetLowPowerStatsImpl = getPowerStatsHalLowPowerDataLocked;
-        gGetPlatformLowPowerStatsImpl = getPowerStatsHalPlatformDataLocked;
-        gGetSubsystemLowPowerStatsImpl = getPowerStatsHalSubsystemDataLocked;
         gGetRailEnergyPowerStatsImpl = getPowerStatsHalRailEnergyDataLocked;
-    } else if (IPowerV1_0::getService() != nullptr) {
-        ALOGI("Using power HAL");
-        gGetLowPowerStatsImpl = getPowerHalLowPowerData;
-        gGetPlatformLowPowerStatsImpl = getPowerHalPlatformData;
-        gGetSubsystemLowPowerStatsImpl = getPowerHalSubsystemData;
+    } else {
         gGetRailEnergyPowerStatsImpl = NULL;
     }
 }
 
-static void getLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrpmStats) {
-    if (jrpmStats == NULL) {
-        jniThrowException(env, "java/lang/NullPointerException",
-                "The rpmstats jni input jobject jrpmStats is null.");
-        return;
-    }
-    if (jgetAndUpdatePlatformState == NULL || jgetSubsystem == NULL
-            || jputVoter == NULL || jputState == NULL) {
-        ALOGE("A rpmstats jni jmethodID is null.");
-        return;
-    }
-
-    std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
-    if (!gGetLowPowerStatsImpl) {
-        setUpPowerStatsLocked();
-    }
-
-    if (gGetLowPowerStatsImpl) {
-        return gGetLowPowerStatsImpl(env, jrpmStats);
-    }
-
-    ALOGE("Unable to load Power Hal or power.stats HAL");
-    return;
-}
-
-static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
-    if (outBuf == NULL) {
-        jniThrowException(env, "java/lang/NullPointerException", "null argument");
-        return -1;
-    }
-
-    std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
-    if (!gGetPlatformLowPowerStatsImpl) {
-        setUpPowerStatsLocked();
-    }
-
-    if (gGetPlatformLowPowerStatsImpl) {
-        return gGetPlatformLowPowerStatsImpl(env, outBuf);
-    }
-
-    ALOGE("Unable to load Power Hal or power.stats HAL");
-    return -1;
-}
-
-static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
-    if (outBuf == NULL) {
-        jniThrowException(env, "java/lang/NullPointerException", "null argument");
-        return -1;
-    }
-
-    std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
-    if (!gGetSubsystemLowPowerStatsImpl) {
-        setUpPowerStatsLocked();
-    }
-
-    if (gGetSubsystemLowPowerStatsImpl) {
-        return gGetSubsystemLowPowerStatsImpl(env, outBuf);
-    }
-
-    ALOGE("Unable to load Power Hal or power.stats HAL");
-    return -1;
-}
-
 static void getRailEnergyPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrailStats) {
     if (jrailStats == NULL) {
         jniThrowException(env, "java/lang/NullPointerException",
@@ -920,9 +347,6 @@
 
 static const JNINativeMethod method_table[] = {
     { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
-    { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats },
-    { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
-    { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats },
     { "getRailEnergyPowerStats", "(Lcom/android/internal/os/RailStats;)V",
         (void*)getRailEnergyPowerStats },
 };
@@ -930,24 +354,10 @@
 int register_android_server_BatteryStatsService(JNIEnv *env)
 {
     // get java classes and methods
-    jclass clsRpmStats = env->FindClass("com/android/internal/os/RpmStats");
-    jclass clsPowerStatePlatformSleepState =
-            env->FindClass("com/android/internal/os/RpmStats$PowerStatePlatformSleepState");
-    jclass clsPowerStateSubsystem =
-            env->FindClass("com/android/internal/os/RpmStats$PowerStateSubsystem");
     jclass clsRailStats = env->FindClass("com/android/internal/os/RailStats");
-    if (clsRpmStats == NULL || clsPowerStatePlatformSleepState == NULL
-            || clsPowerStateSubsystem == NULL || clsRailStats == NULL) {
+    if (clsRailStats == NULL) {
         ALOGE("A rpmstats jni jclass is null.");
     } else {
-        jgetAndUpdatePlatformState = env->GetMethodID(clsRpmStats, "getAndUpdatePlatformState",
-                "(Ljava/lang/String;JI)Lcom/android/internal/os/RpmStats$PowerStatePlatformSleepState;");
-        jgetSubsystem = env->GetMethodID(clsRpmStats, "getSubsystem",
-                "(Ljava/lang/String;)Lcom/android/internal/os/RpmStats$PowerStateSubsystem;");
-        jputVoter = env->GetMethodID(clsPowerStatePlatformSleepState, "putVoter",
-                "(Ljava/lang/String;JI)V");
-        jputState = env->GetMethodID(clsPowerStateSubsystem, "putState",
-                "(Ljava/lang/String;JI)V");
         jupdateRailData = env->GetMethodID(clsRailStats, "updateRailData",
                 "(JLjava/lang/String;Ljava/lang/String;JJ)V");
         jsetRailStatsAvailability = env->GetMethodID(clsRailStats, "setRailStatsAvailability",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 1194099..160033e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -16,6 +16,7 @@
 package com.android.server.devicepolicy;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.admin.DevicePolicySafetyChecker;
 import android.app.admin.FullyManagedDeviceProvisioningParams;
 import android.app.admin.IDevicePolicyManager;
@@ -120,11 +121,14 @@
             @NonNull String callerPackage, @NonNull String enterpriseId, int userId) {}
 
     public UserHandle createAndProvisionManagedProfile(
-            @NonNull ManagedProfileProvisioningParams provisioningParams) {
+            @NonNull ManagedProfileProvisioningParams provisioningParams, String callerPackage) {
         return null;
     }
 
     public void provisionFullyManagedDevice(
-            FullyManagedDeviceProvisioningParams provisioningParams) {
+            FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
+    }
+
+    public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4b1bae4..717e77b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -92,6 +92,7 @@
 import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
 import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
 import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
+import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -128,6 +129,7 @@
 import android.accounts.AccountManagerFuture;
 import android.accounts.AuthenticatorException;
 import android.accounts.OperationCanceledException;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -157,6 +159,7 @@
 import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
 import android.app.admin.DevicePolicyManager.PasswordComplexity;
 import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.DevicePolicySafetyChecker;
 import android.app.admin.DeviceStateCache;
@@ -558,6 +561,21 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long USE_SET_LOCATION_ENABLED = 117835097L;
 
+    // Only add to the end of the list. Do not change or rearrange these values, that will break
+    // historical data. Do not use negative numbers or zero, logger only handles positive
+    // integers.
+    private static final int COPY_ACCOUNT_SUCCEEDED = 1;
+    private static final int COPY_ACCOUNT_FAILED = 2;
+    private static final int COPY_ACCOUNT_TIMED_OUT = 3;
+    private static final int COPY_ACCOUNT_EXCEPTION = 4;
+
+    @IntDef({
+            COPY_ACCOUNT_SUCCEEDED,
+            COPY_ACCOUNT_FAILED,
+            COPY_ACCOUNT_TIMED_OUT,
+            COPY_ACCOUNT_EXCEPTION})
+    private @interface CopyAccountStatus {}
+
     /**
      * Admin apps targeting Android S+ may not use
      * {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality
@@ -892,12 +910,20 @@
     };
 
     protected static class RestrictionsListener implements UserRestrictionsListener {
-        private Context mContext;
+        private final Context mContext;
+        private final UserManagerInternal mUserManagerInternal;
+        private final DevicePolicyManagerService mDpms;
 
-        public RestrictionsListener(Context context) {
+        public RestrictionsListener(
+                Context context,
+                UserManagerInternal userManagerInternal,
+                DevicePolicyManagerService dpms) {
             mContext = context;
+            mUserManagerInternal = userManagerInternal;
+            mDpms = dpms;
         }
 
+        @Override
         public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
                 Bundle prevRestrictions) {
             final boolean newlyDisallowed =
@@ -907,13 +933,19 @@
             final boolean restrictionChanged = (newlyDisallowed != previouslyDisallowed);
 
             if (restrictionChanged) {
-                // Notify ManagedProvisioning to update the built-in cross profile intent filters.
-                Intent intent = new Intent(
-                        DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
-                intent.setPackage(getManagedProvisioningPackage(mContext));
-                intent.putExtra(Intent.EXTRA_USER_ID, userId);
-                intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+                final int parentId = mUserManagerInternal.getProfileParentId(userId);
+                if (parentId == userId) {
+                    return;
+                }
+
+                // Always reset filters on the parent user, which handles cross profile intent
+                // filters between the parent and its profiles.
+                Slog.i(LOG_TAG, "Resetting cross-profile intent filters on restriction "
+                        + "change");
+                mDpms.resetDefaultCrossProfileIntentFilters(parentId);
+                mContext.sendBroadcastAsUser(new Intent(
+                                DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED),
+                        UserHandle.of(userId));
             }
         }
     }
@@ -1067,30 +1099,35 @@
      * @throws UnsafeStateException if it's not safe to execute the operation.
      */
     private void checkCanExecuteOrThrowUnsafe(@DevicePolicyOperation int operation) {
-        if (!canExecute(operation)) {
-            if (mSafetyChecker == null) {
-                // Happens on CTS after it's set just once (by OneTimeSafetyChecker)
-                throw new UnsafeStateException(operation);
-            }
-            // Let mSafetyChecker customize it (for example, by explaining how to retry)
-            throw mSafetyChecker.newUnsafeStateException(operation);
+        int reason = getUnsafeOperationReason(operation);
+        if (reason == UNSAFE_OPERATION_REASON_NONE) return;
+
+        if (mSafetyChecker == null) {
+            // Happens on CTS after it's set just once (by OneTimeSafetyChecker)
+            throw new UnsafeStateException(operation, reason);
         }
+        // Let mSafetyChecker customize it (for example, by explaining how to retry)
+        throw mSafetyChecker.newUnsafeStateException(operation, reason);
     }
 
     /**
-     * Returns whether it's safe to execute the given {@code operation}.
+     * Returns whether it's safe to execute the given {@code operation}, and why.
      */
-    boolean canExecute(@DevicePolicyOperation int operation) {
-        return mSafetyChecker == null || mSafetyChecker.isDevicePolicyOperationSafe(operation);
+    @UnsafeOperationReason
+    int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
+        return mSafetyChecker == null ? UNSAFE_OPERATION_REASON_NONE
+                : mSafetyChecker.getUnsafeOperationReason(operation);
     }
 
     @Override
-    public void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) {
+    public void setNextOperationSafety(@DevicePolicyOperation int operation,
+            @UnsafeOperationReason int reason) {
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
-        Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %b)",
-                DevicePolicyManager.operationToString(operation), safe));
-        mSafetyChecker = new OneTimeSafetyChecker(this, operation, safe);
+        Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %s)",
+                DevicePolicyManager.operationToString(operation),
+                DevicePolicyManager.unsafeOperationReasonToString(reason)));
+        mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason);
     }
 
     /**
@@ -1598,7 +1635,8 @@
 
         mSetupContentObserver = new SetupContentObserver(mHandler);
 
-        mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext));
+        mUserManagerInternal.addUserRestrictionsListener(
+                new RestrictionsListener(mContext, mUserManagerInternal, this));
         mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
 
         loadOwners();
@@ -15927,11 +15965,12 @@
 
     @Override
     public UserHandle createAndProvisionManagedProfile(
-            @NonNull ManagedProfileProvisioningParams provisioningParams) {
+            @NonNull ManagedProfileProvisioningParams provisioningParams,
+            @NonNull String callerPackage) {
         final ComponentName admin = provisioningParams.getProfileAdminComponentName();
         Objects.requireNonNull(admin, "admin is null");
 
-        final CallerIdentity caller = getCallerIdentity();
+        final CallerIdentity caller = getCallerIdentity(callerPackage);
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
 
@@ -15946,6 +15985,7 @@
                         "Provisioning preconditions failed with result: " + result);
             }
 
+            final long startTime = SystemClock.elapsedRealtime();
             final Set<String> nonRequiredApps = provisioningParams.isLeaveAllSystemAppsEnabled()
                     ? Collections.emptySet()
                     : mOverlayPackagesProvider.getNonRequiredApps(
@@ -15962,8 +16002,12 @@
                         "Error creating profile, createProfileForUserEvenWhenDisallowed "
                                 + "returned null.");
             }
-
             resetInteractAcrossProfilesAppOps();
+            logEventDuration(
+                    DevicePolicyEnums.PLATFORM_PROVISIONING_CREATE_PROFILE_MS,
+                    startTime,
+                    callerPackage);
+
             installExistingAdminPackage(userInfo.id, admin.getPackageName());
             if (!enableAdminAndSetProfileOwner(
                     userInfo.id, caller.getUserId(), admin, provisioningParams.getOwnerName())) {
@@ -15973,28 +16017,22 @@
             }
             setUserSetupComplete(userInfo.id);
 
-            startUser(userInfo.id);
+            startUser(userInfo.id, callerPackage);
             maybeMigrateAccount(
                     userInfo.id, caller.getUserId(), provisioningParams.getAccountToMigrate(),
-                    provisioningParams.isKeepAccountMigrated());
+                    provisioningParams.isKeepAccountMigrated(), callerPackage);
 
             if (provisioningParams.isOrganizationOwnedProvisioning()) {
-                markIsProfileOwnerOnOrganizationOwnedDevice(admin, userInfo.id);
-                restrictRemovalOfManagedProfile(admin, userInfo.id);
+                setProfileOwnerOnOrgOwnedDeviceState(admin, userInfo.id, caller.getUserId());
             }
 
-            final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_PROFILE_CREATED)
-                    .putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id)
-                    .putExtra(
-                            DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
-                            provisioningParams.isLeaveAllSystemAppsEnabled())
-                    .setPackage(getManagedProvisioningPackage(mContext))
-                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-
             return userInfo.getUserHandle();
         } catch (Exception e) {
-            // in case of any errors during provisioning, remove the newly created profile.
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
+                    .setStrings(callerPackage)
+                    .write();
+            // In case of any errors during provisioning, remove the newly created profile.
             if (userInfo != null) {
                 mUserManager.removeUserEvenWhenDisallowed(userInfo.id);
             }
@@ -16099,7 +16137,9 @@
                 mContext.getContentResolver(), USER_SETUP_COMPLETE, 1, userId);
     }
 
-    private void startUser(@UserIdInt int userId) throws IllegalStateException {
+    private void startUser(@UserIdInt int userId, String callerPackage)
+            throws IllegalStateException {
+        final long startTime = SystemClock.elapsedRealtime();
         final UserUnlockedBlockingReceiver unlockedReceiver = new UserUnlockedBlockingReceiver(
                 userId);
         mContext.registerReceiverAsUser(
@@ -16118,6 +16158,10 @@
                 throw new ServiceSpecificException(PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
                         String.format("Timeout whilst waiting for unlock of user %d.", userId));
             }
+            logEventDuration(
+                    DevicePolicyEnums.PLATFORM_PROVISIONING_START_PROFILE_MS,
+                    startTime,
+                    callerPackage);
         } catch (RemoteException e) {
             // Shouldn't happen.
         } finally {
@@ -16125,9 +16169,9 @@
         }
     }
 
-    void maybeMigrateAccount(
+    private void maybeMigrateAccount(
             @UserIdInt int targetUserId, @UserIdInt int sourceUserId, Account accountToMigrate,
-            boolean keepAccountMigrated) {
+            boolean keepAccountMigrated, String callerPackage) {
         final UserHandle sourceUser = UserHandle.of(sourceUserId);
         final UserHandle targetUser = UserHandle.of(targetUserId);
         if (accountToMigrate == null) {
@@ -16138,13 +16182,16 @@
             Slog.w(LOG_TAG, "sourceUser and targetUser are the same, won't migrate account.");
             return;
         }
-        copyAccount(targetUser, sourceUser, accountToMigrate);
+        copyAccount(targetUser, sourceUser, accountToMigrate, callerPackage);
         if (!keepAccountMigrated) {
             removeAccount(accountToMigrate);
         }
     }
 
-    void copyAccount(UserHandle targetUser, UserHandle sourceUser, Account accountToMigrate) {
+    private void copyAccount(
+            UserHandle targetUser, UserHandle sourceUser, Account accountToMigrate,
+            String callerPackage) {
+        final long startTime = SystemClock.elapsedRealtime();
         try {
             final AccountManager accountManager = mContext.getSystemService(AccountManager.class);
             final boolean copySucceeded = accountManager.copyAccountToUser(
@@ -16153,16 +16200,35 @@
                     targetUser,
                     /* callback= */ null, /* handler= */ null)
                     .getResult(60 * 3, TimeUnit.SECONDS);
-            if (!copySucceeded) {
+            if (copySucceeded) {
+                logCopyAccountStatus(COPY_ACCOUNT_SUCCEEDED, callerPackage);
+                logEventDuration(
+                        DevicePolicyEnums.PLATFORM_PROVISIONING_COPY_ACCOUNT_MS,
+                        startTime,
+                        callerPackage);
+            } else {
+                logCopyAccountStatus(COPY_ACCOUNT_FAILED, callerPackage);
                 Slog.e(LOG_TAG, "Failed to copy account to " + targetUser);
             }
-        } catch (OperationCanceledException | AuthenticatorException | IOException e) {
+        } catch (OperationCanceledException e) {
             // Account migration is not considered a critical operation.
+            logCopyAccountStatus(COPY_ACCOUNT_TIMED_OUT, callerPackage);
+            Slog.e(LOG_TAG, "Exception copying account to " + targetUser, e);
+        } catch (AuthenticatorException | IOException e) {
+            logCopyAccountStatus(COPY_ACCOUNT_EXCEPTION, callerPackage);
             Slog.e(LOG_TAG, "Exception copying account to " + targetUser, e);
         }
     }
 
-    void removeAccount(Account account) {
+    private static void logCopyAccountStatus(@CopyAccountStatus int status, String callerPackage) {
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_COPY_ACCOUNT_STATUS)
+                .setInt(status)
+                .setStrings(callerPackage)
+                .write();
+    }
+
+    private void removeAccount(Account account) {
         final AccountManager accountManager =
                 mContext.getSystemService(AccountManager.class);
         final AccountManagerFuture<Bundle> bundle = accountManager.removeAccount(account,
@@ -16189,26 +16255,25 @@
         }
     }
 
-    private void markIsProfileOwnerOnOrganizationOwnedDevice(
-            ComponentName admin, @UserIdInt int profileId) {
-        getDpmForProfile(profileId).markProfileOwnerOnOrganizationOwnedDevice(admin);
+    private void setProfileOwnerOnOrgOwnedDeviceState(
+            ComponentName admin, @UserIdInt int profileId, @UserIdInt int parentUserId) {
+        synchronized (getLockObject()) {
+            markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, profileId);
+        }
+        restrictRemovalOfManagedProfile(parentUserId);
     }
 
-    private void restrictRemovalOfManagedProfile(
-            ComponentName admin, @UserIdInt int profileId) {
-        getDpmForProfile(profileId).addUserRestriction(
-                admin, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
-    }
-
-    private DevicePolicyManager getDpmForProfile(@UserIdInt int profileId) {
-        final Context profileContext = mContext.createContextAsUser(
-                UserHandle.of(profileId), /* flags= */ 0);
-        return profileContext.getSystemService(DevicePolicyManager.class);
+    private void restrictRemovalOfManagedProfile(@UserIdInt int parentUserId) {
+        final UserHandle parentUserHandle = UserHandle.of(parentUserId);
+        mUserManager.setUserRestriction(
+                UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                /* value= */ true,
+                parentUserHandle);
     }
 
     @Override
     public void provisionFullyManagedDevice(
-            FullyManagedDeviceProvisioningParams provisioningParams) {
+            FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
         ComponentName deviceAdmin = provisioningParams.getDeviceAdminComponentName();
 
         Objects.requireNonNull(deviceAdmin, "admin is null.");
@@ -16252,15 +16317,12 @@
             }
 
             disallowAddUser();
-
-            final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONED_MANAGED_DEVICE)
-                    .putExtra(Intent.EXTRA_USER_HANDLE, caller.getUserId())
-                    .putExtra(
-                            DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
-                            provisioningParams.isLeaveAllSystemAppsEnabled())
-                    .setPackage(getManagedProvisioningPackage(mContext))
-                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+        } catch (Exception e) {
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
+                    .setStrings(callerPackage)
+                    .write();
+            throw e;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -16344,4 +16406,54 @@
         }
         return true;
     }
+
+    private static void logEventDuration(int eventId, long startTime, String callerPackage) {
+        final long duration = SystemClock.elapsedRealtime() - startTime;
+        DevicePolicyEventLogger
+                .createEvent(eventId)
+                .setTimePeriod(duration)
+                .setStrings(callerPackage)
+                .write();
+    }
+
+    @Override
+    public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            try {
+                final List<UserInfo> profiles = mUserManager.getProfiles(userId);
+                final int numOfProfiles = profiles.size();
+                if (numOfProfiles <= 1) {
+                    return;
+                }
+
+                final String managedProvisioningPackageName = getManagedProvisioningPackage(
+                        mContext);
+                // Removes cross profile intent filters from the parent to all the profiles.
+                mIPackageManager.clearCrossProfileIntentFilters(
+                        userId, mContext.getOpPackageName());
+                // Setting and resetting default cross profile intent filters was previously handled
+                // by Managed Provisioning. For backwards compatibility, clear any intent filters
+                // that were set by ManagedProvisioning.
+                mIPackageManager.clearCrossProfileIntentFilters(
+                        userId, managedProvisioningPackageName);
+
+                // For each profile reset cross profile intent filters
+                for (int i = 0; i < numOfProfiles; i++) {
+                    UserInfo profile = profiles.get(i);
+                    mIPackageManager.clearCrossProfileIntentFilters(
+                            profile.id, mContext.getOpPackageName());
+                    // Clear any intent filters that were set by ManagedProvisioning.
+                    mIPackageManager.clearCrossProfileIntentFilters(
+                            profile.id, managedProvisioningPackageName);
+
+                    mUserManagerInternal.setDefaultCrossProfileIntentFilters(userId, profile.id);
+                }
+            } catch (RemoteException e) {
+                // Shouldn't happen.
+            }
+        });
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 22866b4..fc1d831 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -73,25 +73,28 @@
         pw.printf("    Prints this help text.\n\n");
         pw.printf("  %s <OPERATION_ID>\n", CMD_IS_SAFE_OPERATION);
         pw.printf("    Checks if the give operation is safe \n\n");
-        pw.printf("  %s <OPERATION_ID> <true|false>\n", CMD_SET_SAFE_OPERATION);
+        pw.printf("  %s <OPERATION_ID> <REASON_ID>\n", CMD_SET_SAFE_OPERATION);
         pw.printf("    Emulates the result of the next call to check if the given operation is safe"
                 + " \n\n");
     }
 
     private int runIsSafeOperation(PrintWriter pw) {
         int operation = Integer.parseInt(getNextArgRequired());
-        boolean safe = mService.canExecute(operation);
-        pw.printf("Operation %s is %s\n", DevicePolicyManager.operationToString(operation),
-                safe ? "SAFE" : "UNSAFE");
+        int reason = mService.getUnsafeOperationReason(operation);
+        boolean safe = reason == DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
+        pw.printf("Operation %s is %b. Reason: %s\n",
+                DevicePolicyManager.operationToString(operation), safe,
+                DevicePolicyManager.unsafeOperationReasonToString(reason));
         return 0;
     }
 
     private int runSetSafeOperation(PrintWriter pw) {
         int operation = Integer.parseInt(getNextArgRequired());
-        boolean safe = getNextArg().equals("true");
-        mService.setNextOperationSafety(operation, safe);
+        int reason = Integer.parseInt(getNextArgRequired());
+        mService.setNextOperationSafety(operation, reason);
         pw.printf("Next call to check operation %s will return %s\n",
-                DevicePolicyManager.operationToString(operation), safe ? "SAFE" : "UNSAFE");
+                DevicePolicyManager.operationToString(operation),
+                DevicePolicyManager.unsafeOperationReasonToString(reason));
         return 0;
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
index f7a8261..883f95d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
@@ -15,9 +15,12 @@
  */
 package com.android.server.devicepolicy;
 
+import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
 import static android.app.admin.DevicePolicyManager.operationToString;
+import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
 
 import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
 import android.app.admin.DevicePolicySafetyChecker;
 import android.util.Slog;
 
@@ -40,31 +43,33 @@
     private final DevicePolicyManagerService mService;
     private final DevicePolicySafetyChecker mRealSafetyChecker;
     private final @DevicePolicyOperation int mOperation;
-    private final boolean mSafe;
+    private final @UnsafeOperationReason int mReason;
 
     OneTimeSafetyChecker(DevicePolicyManagerService service,
-            @DevicePolicyOperation int operation, boolean safe) {
+            @DevicePolicyOperation int operation, @UnsafeOperationReason int reason) {
         mService = Objects.requireNonNull(service);
         mOperation = operation;
-        mSafe = safe;
+        mReason = reason;
         mRealSafetyChecker = service.getDevicePolicySafetyChecker();
         Slog.i(TAG, "Saving real DevicePolicySafetyChecker as " + mRealSafetyChecker);
     }
 
     @Override
-    public boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation) {
+    @UnsafeOperationReason
+    public int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
         String name = operationToString(operation);
-        boolean safe = true;
+        int reason = UNSAFE_OPERATION_REASON_NONE;
         if (operation == mOperation) {
-            safe = mSafe;
+            reason = mReason;
         } else {
             Slog.wtf(TAG, "invalid call to isDevicePolicyOperationSafe(): asked for " + name
                     + ", should be " + operationToString(mOperation));
         }
-        Slog.i(TAG, "isDevicePolicyOperationSafe(" + name + "): returning " + safe
+        Slog.i(TAG, "getDevicePolicyOperationSafety(" + name + "): returning "
+                + unsafeOperationReasonToString(reason)
                 + " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker);
         mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker);
-        return safe;
+        return reason;
     }
 
     @Override
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index b2efc71..42360d8 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -88,7 +88,6 @@
     }
     sp<ProcessState> ps(ProcessState::self());
     ps->startThreadPool();
-    ps->giveThreadPoolName();
     // sm->addService increments the reference count, and now we're OK with returning the pointer.
     return self.get();
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 636be4a..7a4c611 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -147,6 +147,7 @@
 import com.android.server.om.OverlayManagerService;
 import com.android.server.os.BugreportManagerService;
 import com.android.server.os.DeviceIdentifiersPolicyService;
+import com.android.server.os.NativeTombstoneManagerService;
 import com.android.server.os.SchedulingPolicyService;
 import com.android.server.people.PeopleService;
 import com.android.server.pm.BackgroundDexOptService;
@@ -160,6 +161,7 @@
 import com.android.server.pm.ShortcutService;
 import com.android.server.pm.UserManagerService;
 import com.android.server.pm.dex.SystemServerDexLoadReporter;
+import com.android.server.pm.verify.domain.DomainVerificationService;
 import com.android.server.policy.PermissionPolicyService;
 import com.android.server.policy.PhoneWindowManager;
 import com.android.server.policy.role.RoleServicePlatformHelperImpl;
@@ -206,12 +208,15 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Timer;
+import java.util.TreeSet;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Future;
 
@@ -480,6 +485,50 @@
 
     private static native void fdtrackAbort();
 
+    private static final File HEAP_DUMP_PATH = new File("/data/system/heapdump/");
+    private static final int MAX_HEAP_DUMPS = 2;
+
+    /**
+     * Dump system_server's heap.
+     *
+     * For privacy reasons, these aren't automatically pulled into bugreports:
+     * they must be manually pulled by the user.
+     */
+    private static void dumpHprof() {
+        // hprof dumps are rather large, so ensure we don't fill the disk by generating
+        // hundreds of these that will live forever.
+        TreeSet<File> existingTombstones = new TreeSet<>();
+        for (File file : HEAP_DUMP_PATH.listFiles()) {
+            if (!file.isFile()) {
+                continue;
+            }
+            if (!file.getName().startsWith("fdtrack-")) {
+                continue;
+            }
+            existingTombstones.add(file);
+        }
+        if (existingTombstones.size() >= MAX_HEAP_DUMPS) {
+            for (int i = 0; i < MAX_HEAP_DUMPS - 1; ++i) {
+                // Leave the newest `MAX_HEAP_DUMPS - 1` tombstones in place.
+                existingTombstones.pollLast();
+            }
+            for (File file : existingTombstones) {
+                if (!file.delete()) {
+                    Slog.w("System", "Failed to clean up hprof " + file);
+                }
+            }
+        }
+
+        try {
+            String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
+            String filename = "/data/system/heapdump/fdtrack-" + date + ".hprof";
+            Debug.dumpHprofData(filename);
+        } catch (IOException ex) {
+            Slog.e("System", "Failed to dump fdtrack hprof");
+            ex.printStackTrace();
+        }
+    }
+
     /**
      * Spawn a thread that monitors for fd leaks.
      */
@@ -504,6 +553,7 @@
                     enabled = true;
                 } else if (maxFd > abortThreshold) {
                     Slog.i("System", "fdtrack abort threshold reached, dumping and aborting");
+                    dumpHprof();
                     fdtrackAbort();
                 }
 
@@ -1060,11 +1110,18 @@
                     SystemClock.elapsedRealtime());
         }
 
+        t.traceBegin("StartDomainVerificationService");
+        DomainVerificationService domainVerificationService = new DomainVerificationService(
+                mSystemContext, SystemConfig.getInstance(), platformCompat);
+        mSystemServiceManager.startService(domainVerificationService);
+        t.traceEnd();
+
         t.traceBegin("StartPackageManagerService");
         try {
             Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
             mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
-                    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
+                    domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
+                    mOnlyCore);
         } finally {
             Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
         }
@@ -1209,6 +1266,11 @@
         mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS);
         t.traceEnd();
 
+        // Tracks native tombstones.
+        t.traceBegin("StartNativeTombstoneManagerService");
+        mSystemServiceManager.startService(NativeTombstoneManagerService.class);
+        t.traceEnd();
+
         // Service to capture bugreports.
         t.traceBegin("StartBugreportManagerService");
         mSystemServiceManager.startService(BugreportManagerService.class);
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 21c863d..6c5c1d4 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -54,6 +54,7 @@
 import org.mockito.Mockito.verify
 import org.testng.Assert.assertThrows
 import java.io.File
+import java.util.UUID
 
 @RunWith(Parameterized::class)
 class PackageManagerComponentLabelIconOverrideTest {
@@ -262,8 +263,13 @@
                     .apply(block)
                     .hideAsFinal()
 
-    private fun makePkgSetting(pkgName: String) = spy(PackageSetting(pkgName, null, File("/test"),
-            null, null, null, null, 0, 0, 0, 0, null, null, null)) {
+    private fun makePkgSetting(pkgName: String) = spy(
+        PackageSetting(
+            pkgName, null, File("/test"),
+            null, null, null, null, 0, 0, 0, 0, null, null, null,
+            UUID.fromString("3f9d52b7-d7b4-406a-a1da-d9f19984c72c")
+        )
+    ) {
         this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp
     }
 
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
new file mode 100644
index 0000000..4aa8abc
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2020 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.
+
+android_test {
+    name: "PackageManagerServiceUnitTests",
+    srcs: ["src/**/*.kt"],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.runner",
+        "junit",
+        "services.core",
+        "servicestests-utils",
+        "testng",
+        "truth-prebuilt",
+    ],
+    platform_apis: true,
+    test_suites: ["device-tests"],
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml
new file mode 100644
index 0000000..2ef7a1f
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.server.pm.test">
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.server.pm.test"
+        />
+
+</manifest>
+
diff --git a/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml b/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml
new file mode 100644
index 0000000..78dd1c5
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<configuration description="Test module config for PackageManagerServiceUnitTests">
+    <option name="test-tag" value="PackageManagerServiceUnitTests" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="PackageManagerServiceUnitTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.server.pm.test" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
new file mode 100644
index 0000000..e99b071
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.os.Build
+import android.os.PatternMatcher
+import android.util.ArraySet
+import com.android.server.SystemConfig
+import com.android.server.compat.PlatformCompat
+import com.android.server.pm.verify.domain.DomainVerificationCollector
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
+
+class DomainVerificationCollectorTest {
+
+    companion object {
+        private const val TEST_PKG_NAME = "com.test.pkg"
+    }
+
+    private val platformCompat: PlatformCompat = mockThrowOnUnmocked {
+        whenever(isChangeEnabled(eq(DomainVerificationCollector.RESTRICT_DOMAINS), any())) {
+            (arguments[1] as ApplicationInfo).targetSdkVersion >= Build.VERSION_CODES.S
+        }
+    }
+
+    @Test
+    fun verifyV1() {
+        val pkg = mockPkg(useV2 = false, autoVerify = true)
+        val collector = mockCollector()
+        assertThat(collector.collectAllWebDomains(pkg))
+                .containsExactly("example1.com", "example2.com", "example3.com")
+        assertThat(collector.collectAutoVerifyDomains(pkg))
+                .containsExactly("example1.com", "example2.com", "example3.com", "example4.com")
+    }
+
+    @Test
+    fun verifyV1NoAutoVerify() {
+        val pkg = mockPkg(useV2 = false, autoVerify = false)
+        val collector = mockCollector()
+        assertThat(collector.collectAllWebDomains(pkg))
+                .containsExactly("example1.com", "example2.com", "example3.com")
+        assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+    }
+
+    @Test
+    fun verifyV1ForceAutoVerify() {
+        val pkg = mockPkg(useV2 = false, autoVerify = false)
+        val collector = mockCollector(linkedApps = setOf(TEST_PKG_NAME))
+        assertThat(collector.collectAllWebDomains(pkg))
+                .containsExactly("example1.com", "example2.com", "example3.com")
+        assertThat(collector.collectAutoVerifyDomains(pkg))
+                .containsExactly("example1.com", "example2.com", "example3.com", "example4.com")
+    }
+
+    @Test
+    fun verifyV1NoValidIntentFilter() {
+        val pkg = mockThrowOnUnmocked<AndroidPackage> {
+            whenever(packageName) { TEST_PKG_NAME }
+            whenever(targetSdkVersion) { Build.VERSION_CODES.R }
+
+            val activityList = listOf(
+                    ParsedActivity().apply {
+                        addIntent(
+                                ParsedIntentInfo().apply {
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("http")
+                                    addDataScheme("https")
+                                    addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+                                    addDataAuthority("example1.com", null)
+                                }
+                        )
+                    },
+                    ParsedActivity().apply {
+                        addIntent(
+                                ParsedIntentInfo().apply {
+                                    setAutoVerify(true)
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("http")
+                                    addDataScheme("https")
+
+                                    // The presence of a non-web-scheme as the only autoVerify
+                                    // intent-filter, when non-forced, means that v1 will not pick
+                                    // up the package for verification.
+                                    addDataScheme("nonWebScheme")
+                                    addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+                                    addDataAuthority("example2.com", null)
+                                }
+                        )
+                    },
+            )
+
+            whenever(activities) { activityList }
+        }
+
+        val collector = mockCollector()
+        assertThat(collector.collectAllWebDomains(pkg))
+                .containsExactly("example1.com", "example2.com")
+        assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+    }
+
+    @Test
+    fun verifyV2() {
+        val pkg = mockPkg(useV2 = true, autoVerify = true)
+        val collector = mockCollector()
+
+        assertThat(collector.collectAllWebDomains(pkg))
+                .containsExactly("example1.com", "example2.com", "example3.com")
+        assertThat(collector.collectAutoVerifyDomains(pkg))
+                .containsExactly("example1.com", "example3.com")
+    }
+
+    @Test
+    fun verifyV2NoAutoVerify() {
+        val pkg = mockPkg(useV2 = true, autoVerify = false)
+        val collector = mockCollector()
+
+        assertThat(collector.collectAllWebDomains(pkg))
+                .containsExactly("example1.com", "example2.com", "example3.com")
+        assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+    }
+
+    @Test
+    fun verifyV2ForceAutoVerifyIgnored() {
+        val pkg = mockPkg(useV2 = true, autoVerify = false)
+        val collector = mockCollector(linkedApps = setOf(TEST_PKG_NAME))
+
+        assertThat(collector.collectAllWebDomains(pkg))
+                .containsExactly("example1.com", "example2.com", "example3.com")
+        assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+    }
+
+    private fun mockCollector(linkedApps: Set<String> = emptySet()): DomainVerificationCollector {
+        val systemConfig = mockThrowOnUnmocked<SystemConfig> {
+            whenever(this.linkedApps) { ArraySet(linkedApps) }
+        }
+
+        return DomainVerificationCollector(platformCompat, systemConfig)
+    }
+
+    private fun mockPkg(useV2: Boolean, autoVerify: Boolean): AndroidPackage {
+        // Translate equivalent of the following manifest declaration. This string isn't actually
+        // parsed, but it's a far easier to read representation of the test data.
+        // language=XML
+        """
+            <xml>
+                <intent-filter android:autoVerify="$autoVerify">
+                    <action android:name="android.intent.action.VIEW"/>
+                    <category android:name="android.intent.category.BROWSABLE"/>
+                    <category android:name="android.intent.category.DEFAULT"/>
+                    <data android:scheme="http"/>
+                    <data android:scheme="https"/>
+                    <data android:path="/sub"/>
+                    <data android:host="example1.com"/>
+                </intent-filter>
+                <intent-filter>
+                    <action android:name="android.intent.action.VIEW"/>
+                    <category android:name="android.intent.category.BROWSABLE"/>
+                    <category android:name="android.intent.category.DEFAULT"/>
+                    <data android:scheme="http"/>
+                    <data android:path="/sub2"/>
+                    <data android:host="example2.com"/>
+                </intent-filter>
+                <intent-filter android:autoVerify="$autoVerify">
+                    <action android:name="android.intent.action.VIEW"/>
+                    <category android:name="android.intent.category.BROWSABLE"/>
+                    <category android:name="android.intent.category.DEFAULT"/>
+                    <data android:scheme="https"/>
+                    <data android:path="/sub3"/>
+                    <data android:host="example3.com"/>
+                </intent-filter>
+                <intent-filter android:autoVerify="$autoVerify">
+                    <action android:name="android.intent.action.VIEW"/>
+                    <category android:name="android.intent.category.BROWSABLE"/>
+                    <data android:scheme="https"/>
+                    <data android:path="/sub4"/>
+                    <data android:host="example4.com"/>
+                </intent-filter>
+                <intent-filter android:autoVerify="$autoVerify">
+                    <action android:name="android.intent.action.VIEW"/>
+                    <category android:name="android.intent.category.DEFAULT"/>
+                    <data android:scheme="https"/>
+                    <data android:path="/sub5"/>
+                    <data android:host="example5.com"/>
+                </intent-filter>
+                <intent-filter android:autoVerify="$autoVerify">
+                    <category android:name="android.intent.category.BROWSABLE"/>
+                    <category android:name="android.intent.category.DEFAULT"/>
+                    <data android:scheme="https"/>
+                    <data android:path="/sub5"/>
+                    <data android:host="example5.com"/>
+                </intent-filter>
+            </xml>
+        """.trimIndent()
+
+        return mockThrowOnUnmocked<AndroidPackage> {
+            whenever(packageName) { TEST_PKG_NAME }
+            whenever(targetSdkVersion) {
+                if (useV2) Build.VERSION_CODES.S else Build.VERSION_CODES.R
+            }
+
+            // The intents are split into separate Activities to test that multiple are collected
+            val activityList = listOf(
+                    ParsedActivity().apply {
+                        addIntent(
+                                ParsedIntentInfo().apply {
+                                    setAutoVerify(autoVerify)
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("http")
+                                    addDataScheme("https")
+                                    addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+                                    addDataAuthority("example1.com", null)
+                                }
+                        )
+                        addIntent(
+                                ParsedIntentInfo().apply {
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("http")
+                                    addDataPath("/sub2", PatternMatcher.PATTERN_LITERAL)
+                                    addDataAuthority("example2.com", null)
+                                }
+                        )
+                    },
+                    ParsedActivity().apply {
+                        addIntent(
+                                ParsedIntentInfo().apply {
+                                    setAutoVerify(autoVerify)
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("https")
+                                    addDataPath("/sub3", PatternMatcher.PATTERN_LITERAL)
+                                    addDataAuthority("example3.com", null)
+                                }
+                        )
+                    },
+                    ParsedActivity().apply {
+                        addIntent(
+                                ParsedIntentInfo().apply {
+                                    setAutoVerify(autoVerify)
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addDataScheme("https")
+                                    addDataPath("/sub4", PatternMatcher.PATTERN_LITERAL)
+                                    addDataAuthority("example4.com", null)
+                                }
+                        )
+                        addIntent(
+                                ParsedIntentInfo().apply {
+                                    setAutoVerify(autoVerify)
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("https")
+                                    addDataPath("/sub5", PatternMatcher.PATTERN_LITERAL)
+                                    addDataAuthority("example5.com", null)
+                                }
+                        )
+                        addIntent(
+                                ParsedIntentInfo().apply {
+                                    setAutoVerify(autoVerify)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("https")
+                                    addDataPath("/sub6", PatternMatcher.PATTERN_LITERAL)
+                                    addDataAuthority("example6.com", null)
+                                }
+                        )
+                    },
+            )
+
+            whenever(activities) { activityList }
+        }
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
new file mode 100644
index 0000000..deb3147
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.pm.verify.domain.DomainVerificationRequest
+import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationUserSelection
+import android.os.Parcel
+import android.os.Parcelable
+import android.os.UserHandle
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.util.UUID
+
+@RunWith(Parameterized::class)
+class DomainVerificationCoreApiTest {
+
+    companion object {
+        private val IS_EQUAL_TO: (value: Any, other: Any) -> Unit = { value, other ->
+            assertThat(value).isEqualTo(other)
+        }
+        private val IS_MAP_EQUAL_TO: (value: Map<*, *>, other: Map<*, *>) -> Unit = { value,
+                                                                                      other ->
+            assertThat(value).containsExactlyEntriesIn(other)
+        }
+
+        @JvmStatic
+        @Parameterized.Parameters
+        fun parameters() = arrayOf(
+            Parameter(
+                initial = {
+                    DomainVerificationRequest(
+                        setOf(
+                            "com.test.pkg.one",
+                            "com.test.pkg.two"
+                        )
+                    )
+                },
+                unparcel = { DomainVerificationRequest.CREATOR.createFromParcel(it) },
+                assertion = { first, second ->
+                    assertAll<DomainVerificationRequest, Set<String>>(first, second,
+                        { it.packageNames }, { it.component1() }) { value, other ->
+                        assertThat(value).containsExactlyElementsIn(other)
+                    }
+                }
+            ),
+            Parameter(
+                initial = {
+                    DomainVerificationInfo(
+                        UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
+                        "com.test.pkg",
+                        mapOf(
+                            "example.com" to 0,
+                            "example.org" to 1,
+                            "example.new" to 1000
+                        )
+                    )
+                },
+                unparcel = { DomainVerificationInfo.CREATOR.createFromParcel(it) },
+                assertion = { first, second ->
+                    assertAll<DomainVerificationInfo, UUID>(first, second,
+                        { it.identifier }, { it.component1() }, IS_EQUAL_TO
+                    )
+                    assertAll<DomainVerificationInfo, String>(first, second,
+                        { it.packageName }, { it.component2() }, IS_EQUAL_TO
+                    )
+                    assertAll<DomainVerificationInfo, Map<String, Int?>>(first, second,
+                        { it.hostToStateMap }, { it.component3() }, IS_MAP_EQUAL_TO
+                    )
+                }
+            ),
+            Parameter(
+                initial = {
+                    DomainVerificationUserSelection(
+                        UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
+                        "com.test.pkg",
+                        UserHandle.of(10),
+                        true,
+                        mapOf(
+                            "example.com" to true,
+                            "example.org" to false,
+                            "example.new" to true
+                        )
+                    )
+                },
+                unparcel = { DomainVerificationUserSelection.CREATOR.createFromParcel(it) },
+                assertion = { first, second ->
+                    assertAll<DomainVerificationUserSelection, UUID>(first, second,
+                        { it.identifier }, { it.component1() }, IS_EQUAL_TO
+                    )
+                    assertAll<DomainVerificationUserSelection, String>(first, second,
+                        { it.packageName }, { it.component2() }, IS_EQUAL_TO
+                    )
+                    assertAll<DomainVerificationUserSelection, UserHandle>(first, second,
+                        { it.user }, { it.component3() }, IS_EQUAL_TO
+                    )
+                    assertAll<DomainVerificationUserSelection, Boolean>(
+                        first, second, { it.isLinkHandlingAllowed },
+                        { it.component4() }, IS_EQUAL_TO
+                    )
+                    assertAll<DomainVerificationUserSelection, Map<String, Boolean>>(
+                        first, second, { it.hostToUserSelectionMap },
+                        { it.component5() }, IS_MAP_EQUAL_TO
+                    )
+                }
+            )
+        )
+
+        class Parameter<T : Parcelable>(
+            val initial: () -> T,
+            val unparcel: (Parcel) -> T,
+            private val assertion: (first: T, second: T) -> Unit
+        ) {
+            @Suppress("UNCHECKED_CAST")
+            fun assert(first: Any, second: Any) = assertion(first as T, second as T)
+        }
+
+        private fun <T> assertAll(vararg values: T, block: (value: T, other: T) -> Unit) {
+            values.indices.drop(1).forEach {
+                block(values[0], values[it])
+            }
+        }
+
+        private fun <T, V : Any> assertAll(
+            first: T,
+            second: T,
+            fieldValue: (T) -> V,
+            componentValue: (T) -> V,
+            assertion: (value: V, other: V) -> Unit
+        ) {
+            val values = arrayOf<Any>(fieldValue(first), fieldValue(second),
+                    componentValue(first), componentValue(second))
+            values.indices.drop(1).forEach {
+                @Suppress("UNCHECKED_CAST")
+                assertion(values[0] as V, values[it] as V)
+            }
+        }
+    }
+
+    @Parameterized.Parameter(0)
+    lateinit var parameter: Parameter<*>
+
+    @Test
+    fun parcel() {
+        val parcel = Parcel.obtain()
+        val initial = parameter.initial()
+        initial.writeToParcel(parcel, 0)
+        parcel.setDataPosition(0)
+
+        val newInitial = parameter.initial()
+        val unparceled = parameter.unparcel(parcel)
+        parameter.assert(newInitial, unparceled)
+
+        assertAll(initial, newInitial, unparceled) { value: Any, other: Any ->
+            assertThat(value).isEqualTo(other)
+        }
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
new file mode 100644
index 0000000..d863194
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageUserState
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.os.Build
+import android.os.Process
+import android.util.ArraySet
+import android.util.Singleton
+import android.util.SparseArray
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.verify.domain.DomainVerificationEnforcer
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.spyThrowOnUnmocked
+import com.android.server.testutils.whenever
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.testng.Assert.assertThrows
+import java.io.File
+import java.util.UUID
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicInteger
+
+private typealias Enforcer = DomainVerificationEnforcer
+
+@RunWith(Parameterized::class)
+class DomainVerificationEnforcerTest {
+
+    val context: Context = InstrumentationRegistry.getInstrumentation().context
+
+    companion object {
+        private val INTERNAL_UIDS = listOf(Process.ROOT_UID, Process.SHELL_UID, Process.SYSTEM_UID)
+        private const val VERIFIER_UID = Process.FIRST_APPLICATION_UID + 1
+        private const val NON_VERIFIER_UID = Process.FIRST_APPLICATION_UID + 2
+
+        private const val TEST_PKG = "com.test"
+
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun parameters(): Array<Any> {
+            val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
+                DomainVerificationEnforcer(it)
+            }
+
+            val mockPkg = mockThrowOnUnmocked<AndroidPackage> {
+                whenever(packageName) { TEST_PKG }
+                whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+                whenever(activities) {
+                    listOf(
+                        ParsedActivity().apply {
+                            addIntent(
+                                ParsedIntentInfo().apply {
+                                    autoVerify = true
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("https")
+                                    addDataAuthority("example.com", null)
+                                }
+                            )
+                        }
+                    )
+                }
+            }
+
+            val uuid = UUID.randomUUID()
+
+            // TODO: PackageSetting field encapsulation to move to whenever(name)
+            val mockPkgSetting = spyThrowOnUnmocked(
+                PackageSetting(
+                    TEST_PKG,
+                    TEST_PKG,
+                    File("/test"),
+                    null,
+                    null,
+                    null,
+                    null,
+                    1,
+                    0,
+                    0,
+                    0,
+                    null,
+                    null,
+                    null,
+                    uuid
+                )
+            ) {
+                whenever(getPkg()) { mockPkg }
+                whenever(domainSetId) { uuid }
+                whenever(userState) {
+                    SparseArray<PackageUserState>().apply {
+                        this[0] = PackageUserState()
+                    }
+                }
+            }
+
+            val makeService: (Context) -> Triple<AtomicInteger, AtomicInteger, DomainVerificationService> =
+                {
+                    val callingUidInt = AtomicInteger(-1)
+                    val callingUserIdInt = AtomicInteger(-1)
+                    Triple(
+                        callingUidInt, callingUserIdInt, DomainVerificationService(
+                            it,
+                            mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+                            mockThrowOnUnmocked {
+                                whenever(
+                                    isChangeEnabled(
+                                        anyLong(),
+                                        any()
+                                    )
+                                ) { true }
+                            }).apply {
+                                setConnection(mockThrowOnUnmocked {
+                                    whenever(callingUid) { callingUidInt.get() }
+                                    whenever(callingUserId) { callingUserIdInt.get() }
+                                    whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting }
+                                    whenever(getPackageLocked(TEST_PKG)) { mockPkg }
+                                    whenever(schedule(anyInt(), any()))
+                                    whenever(scheduleWriteSettings())
+                                })
+                        }
+                    )
+                }
+
+            fun enforcer(
+                type: Type,
+                name: String,
+                block: DomainVerificationEnforcer.(
+                    callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
+                ) -> Unit
+            ) = Params(
+                type,
+                makeEnforcer,
+                name
+            ) { enforcer, callingUid, callingUserId, userId, proxy ->
+                enforcer.block(callingUid, callingUserId, userId, proxy)
+            }
+
+            fun service(
+                type: Type,
+                name: String,
+                block: DomainVerificationService.(
+                    callingUid: Int, callingUserId: Int, userId: Int
+                ) -> Unit
+            ) = Params(
+                type,
+                makeService,
+                name
+            ) { uidAndUserIdAndService, callingUid, callingUserId, userId, proxy ->
+                val (callingUidInt, callingUserIdInt, service) = uidAndUserIdAndService
+                callingUidInt.set(callingUid)
+                callingUserIdInt.set(callingUserId)
+                service.setProxy(proxy)
+                service.addPackage(mockPkgSetting)
+                service.block(callingUid, callingUserId, userId)
+            }
+
+            return arrayOf(
+                enforcer(Type.INTERNAL, "internal") { callingUid, _, _, _ ->
+                    assertInternal(callingUid)
+                },
+                enforcer(Type.QUERENT, "approvedQuerent") { callingUid, _, _, proxy ->
+                    assertApprovedQuerent(callingUid, proxy)
+                },
+                enforcer(Type.VERIFIER, "approvedVerifier") { callingUid, _, _, proxy ->
+                    assertApprovedVerifier(callingUid, proxy)
+                },
+                enforcer(
+                    Type.SELECTOR,
+                    "approvedUserSelector"
+                ) { callingUid, callingUserId, userId, _ ->
+                    assertApprovedUserSelector(callingUid, callingUserId, userId)
+                },
+
+                service(Type.INTERNAL, "setStatusInternalPackageName") { _, _, _ ->
+                    setDomainVerificationStatusInternal(
+                        TEST_PKG,
+                        DomainVerificationManager.STATE_SUCCESS,
+                        ArraySet(setOf("example.com"))
+                    )
+                },
+                service(Type.INTERNAL, "setUserSelectionInternal") { _, _, userId ->
+                    setDomainVerificationUserSelectionInternal(
+                        userId,
+                        TEST_PKG,
+                        false,
+                        ArraySet(setOf("example.com"))
+                    )
+                },
+                service(Type.INTERNAL, "verifyPackages") { _, _, _ ->
+                    verifyPackages(listOf(TEST_PKG), true)
+                },
+                service(Type.INTERNAL, "clearState") { _, _, _ ->
+                    clearDomainVerificationState(listOf(TEST_PKG))
+                },
+                service(Type.INTERNAL, "clearUserSelections") { _, _, userId ->
+                    clearUserSelections(listOf(TEST_PKG), userId)
+                },
+                service(Type.VERIFIER, "getPackageNames") { _, _, _ ->
+                    validVerificationPackageNames
+                },
+                service(Type.QUERENT, "getInfo") { _, _, _ ->
+                    getDomainVerificationInfo(TEST_PKG)
+                },
+                service(Type.VERIFIER, "setStatus") { _, _, _ ->
+                    setDomainVerificationStatus(
+                        uuid,
+                        setOf("example.com"),
+                        DomainVerificationManager.STATE_SUCCESS
+                    )
+                },
+                service(Type.VERIFIER, "setStatusInternalUid") { callingUid, _, _ ->
+                    setDomainVerificationStatusInternal(
+                        callingUid,
+                        uuid,
+                        setOf("example.com"),
+                        DomainVerificationManager.STATE_SUCCESS
+                    )
+                },
+                service(Type.SELECTOR, "setLinkHandlingAllowed") { _, _, _ ->
+                    setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
+                },
+                service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") { _, _, userId ->
+                    setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, userId)
+                },
+                service(Type.SELECTOR, "getUserSelection") { _, _, _ ->
+                    getDomainVerificationUserSelection(TEST_PKG)
+                },
+                service(Type.SELECTOR_USER, "getUserSelectionUserId") { _, _, userId ->
+                    getDomainVerificationUserSelection(TEST_PKG, userId)
+                },
+                service(Type.SELECTOR, "setUserSelection") { _, _, _ ->
+                    setDomainVerificationUserSelection(uuid, setOf("example.com"), true)
+                },
+                service(Type.SELECTOR_USER, "setUserSelectionUserId") { _, _, userId ->
+                    setDomainVerificationUserSelection(uuid, setOf("example.com"), true, userId)
+                },
+            )
+        }
+
+        data class Params<T : Any>(
+            val type: Type,
+            val construct: (context: Context) -> T,
+            val name: String,
+            private val method: (
+                T, callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
+            ) -> Unit
+        ) {
+            override fun toString() = "${type}_$name"
+
+            fun runMethod(
+                target: Any,
+                callingUid: Int,
+                callingUserId: Int,
+                userId: Int,
+                proxy: DomainVerificationProxy
+            ) {
+                @Suppress("UNCHECKED_CAST")
+                method(target as T, callingUid, callingUserId, userId, proxy)
+            }
+        }
+    }
+
+    @Parameterized.Parameter(0)
+    lateinit var params: Params<*>
+
+    private val proxy: DomainVerificationProxy = mockThrowOnUnmocked {
+        whenever(isCallerVerifier(VERIFIER_UID)) { true }
+        whenever(isCallerVerifier(NON_VERIFIER_UID)) { false }
+        whenever(sendBroadcastForPackages(any()))
+    }
+
+    @Test
+    fun verify() {
+        when (params.type) {
+            Type.INTERNAL -> internal()
+            Type.QUERENT -> approvedQuerent()
+            Type.VERIFIER -> approvedVerifier()
+            Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false)
+            Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true)
+        }.run { /*exhaust*/ }
+    }
+
+    fun internal() {
+        val context: Context = mockThrowOnUnmocked()
+        val target = params.construct(context)
+
+        INTERNAL_UIDS.forEach { runMethod(target, it) }
+        assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
+        assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+    }
+
+    fun approvedQuerent() {
+        val allowUserSelection = AtomicBoolean(false)
+        val context: Context = mockThrowOnUnmocked {
+            whenever(
+                enforcePermission(
+                    eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
+                    anyInt(), anyInt(), anyString()
+                )
+            ) {
+                if (!allowUserSelection.get()) {
+                    throw SecurityException()
+                }
+            }
+        }
+        val target = params.construct(context)
+
+        INTERNAL_UIDS.forEach { runMethod(target, it) }
+
+        verifyNoMoreInteractions(context)
+
+        runMethod(target, VERIFIER_UID)
+        assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+
+        allowUserSelection.set(true)
+
+        runMethod(target, NON_VERIFIER_UID)
+    }
+
+    fun approvedVerifier() {
+        val shouldThrow = AtomicBoolean(false)
+        val context: Context = mockThrowOnUnmocked {
+            whenever(
+                enforcePermission(
+                    eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT),
+                    anyInt(), anyInt(), anyString()
+                )
+            ) {
+                if (shouldThrow.get()) {
+                    throw SecurityException()
+                }
+            }
+        }
+        val target = params.construct(context)
+
+        INTERNAL_UIDS.forEach { runMethod(target, it) }
+
+        verifyNoMoreInteractions(context)
+
+        runMethod(target, VERIFIER_UID)
+        assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+
+        shouldThrow.set(true)
+
+        assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
+        assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+    }
+
+    fun approvedUserSelector(verifyCrossUser: Boolean) {
+        val allowUserSelection = AtomicBoolean(true)
+        val allowInteractAcrossUsers = AtomicBoolean(true)
+        val context: Context = mockThrowOnUnmocked {
+            whenever(
+                enforcePermission(
+                    eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
+                    anyInt(), anyInt(), anyString()
+                )
+            ) {
+                if (!allowUserSelection.get()) {
+                    throw SecurityException()
+                }
+            }
+            whenever(
+                enforcePermission(
+                    eq(android.Manifest.permission.INTERACT_ACROSS_USERS),
+                    anyInt(), anyInt(), anyString()
+                )
+            ) {
+                if (!allowInteractAcrossUsers.get()) {
+                    throw SecurityException()
+                }
+            }
+        }
+        val target = params.construct(context)
+
+        fun runEachTestCaseWrapped(
+            callingUserId: Int,
+            targetUserId: Int,
+            block: (testCase: () -> Unit) -> Unit = { it.invoke() }
+        ) {
+            block { runMethod(target, VERIFIER_UID, callingUserId, targetUserId) }
+            block { runMethod(target, NON_VERIFIER_UID, callingUserId, targetUserId) }
+        }
+
+        val callingUserId = 0
+        val notCallingUserId = 1
+
+        runEachTestCaseWrapped(callingUserId, callingUserId)
+        if (verifyCrossUser) {
+            runEachTestCaseWrapped(callingUserId, notCallingUserId)
+        }
+
+        allowInteractAcrossUsers.set(false)
+
+        runEachTestCaseWrapped(callingUserId, callingUserId)
+
+        if (verifyCrossUser) {
+            runEachTestCaseWrapped(callingUserId, notCallingUserId) {
+                assertThrows(SecurityException::class.java, it)
+            }
+        }
+
+        allowUserSelection.set(false)
+
+        runEachTestCaseWrapped(callingUserId, callingUserId) {
+            assertThrows(SecurityException::class.java, it)
+        }
+        if (verifyCrossUser) {
+            runEachTestCaseWrapped(callingUserId, notCallingUserId) {
+                assertThrows(SecurityException::class.java, it)
+            }
+        }
+
+        allowInteractAcrossUsers.set(true)
+
+        runEachTestCaseWrapped(callingUserId, callingUserId) {
+            assertThrows(SecurityException::class.java, it)
+        }
+        if (verifyCrossUser) {
+            runEachTestCaseWrapped(callingUserId, notCallingUserId) {
+                assertThrows(SecurityException::class.java, it)
+            }
+        }
+    }
+
+    private fun runMethod(target: Any, callingUid: Int, callingUserId: Int = 0, userId: Int = 0) {
+        params.runMethod(target, callingUid, callingUserId, userId, proxy)
+    }
+
+    enum class Type {
+        // System/shell only
+        INTERNAL,
+
+        // INTERNAL || domain verification agent || user setting permission holder
+        QUERENT,
+
+        // INTERNAL || domain verification agent
+        VERIFIER,
+
+        // Holding the user setting permission
+        SELECTOR,
+
+        // Holding the user setting permission, but targeting cross user
+        SELECTOR_USER
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
new file mode 100644
index 0000000..9a3bd99
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.pm.IntentFilterVerificationInfo
+import android.content.pm.PackageManager
+import android.util.ArraySet
+import com.android.server.pm.verify.domain.DomainVerificationLegacySettings
+import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.readXml
+import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.writeXml
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+
+class DomainVerificationLegacySettingsTest {
+
+    @Rule
+    @JvmField
+    val tempFolder = TemporaryFolder()
+
+    @Test
+    fun writeAndReadBackNormal() {
+        val settings = DomainVerificationLegacySettings().apply {
+            add(
+                "com.test.one",
+                IntentFilterVerificationInfo(
+                    "com.test.one",
+                    ArraySet(setOf("example1.com", "example2.com"))
+                ).apply {
+                    status = PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK
+                }
+            )
+            add(
+                "com.test.one",
+                0, PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
+            )
+            add(
+                "com.test.one",
+                10, PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER
+            )
+
+            add(
+                "com.test.two",
+                IntentFilterVerificationInfo(
+                    "com.test.two",
+                    ArraySet(setOf("example3.com"))
+                )
+            )
+
+            add(
+                "com.test.three",
+                11, PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
+            )
+        }
+
+
+        val file = tempFolder.newFile().writeXml(settings::writeSettings)
+        val newSettings = file.readXml {
+            DomainVerificationLegacySettings().apply {
+                readSettings(it)
+            }
+        }
+
+        val xml = file.readText()
+
+        // Legacy migrated settings doesn't bother writing the legacy verification info
+        assertWithMessage(xml).that(newSettings.remove("com.test.one")).isNull()
+        assertWithMessage(xml).that(newSettings.getUserState("com.test.one", 0))
+            .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS)
+        assertWithMessage(xml).that(newSettings.getUserState("com.test.one", 10))
+            .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)
+
+        val firstUserStates = newSettings.getUserStates("com.test.one")
+        assertWithMessage(xml).that(firstUserStates).isNotNull()
+        assertWithMessage(xml).that(firstUserStates!!.size()).isEqualTo(2)
+        assertWithMessage(xml).that(firstUserStates[0])
+            .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS)
+        assertWithMessage(xml).that(firstUserStates[10])
+            .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)
+
+        assertWithMessage(xml).that(newSettings.remove("com.test.two")).isNull()
+        assertWithMessage(xml).that(newSettings.getUserStates("com.test.two")).isNull()
+
+        assertWithMessage(xml).that(newSettings.remove("com.test.three")).isNull()
+        assertWithMessage(xml).that(newSettings.getUserState("com.test.three", 11))
+            .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS)
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
new file mode 100644
index 0000000..a76d8ce
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.pm.verify.domain.DomainVerificationRequest
+import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationUserSelection
+import com.android.server.pm.verify.domain.DomainVerificationPersistence
+
+operator fun <F> android.util.Pair<F, *>.component1() = first
+operator fun <S> android.util.Pair<*, S>.component2() = second
+
+operator fun DomainVerificationRequest.component1() = packageNames
+
+operator fun DomainVerificationInfo.component1() = identifier
+operator fun DomainVerificationInfo.component2() = packageName
+operator fun DomainVerificationInfo.component3() = hostToStateMap
+
+operator fun DomainVerificationUserSelection.component1() = identifier
+operator fun DomainVerificationUserSelection.component2() = packageName
+operator fun DomainVerificationUserSelection.component3() = user
+operator fun DomainVerificationUserSelection.component4() = isLinkHandlingAllowed
+operator fun DomainVerificationUserSelection.component5() = hostToUserSelectionMap
+
+operator fun DomainVerificationPersistence.ReadResult.component1() = active
+operator fun DomainVerificationPersistence.ReadResult.component2() = restored
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
new file mode 100644
index 0000000..a76152c
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.util.ArrayMap
+import android.util.TypedXmlPullParser
+import android.util.TypedXmlSerializer
+import android.util.Xml
+import com.android.server.pm.verify.domain.DomainVerificationPersistence
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState
+import com.android.server.pm.verify.domain.models.DomainVerificationStateMap
+import com.android.server.pm.verify.domain.models.DomainVerificationUserState
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import java.io.File
+import java.nio.charset.StandardCharsets
+import java.util.UUID
+
+class DomainVerificationPersistenceTest {
+
+    companion object {
+        private val PKG_PREFIX = DomainVerificationPersistenceTest::class.java.`package`!!.name
+
+        internal fun File.writeXml(block: (serializer: TypedXmlSerializer) -> Unit) = apply {
+            outputStream().use {
+                // Explicitly use string based XML so it can printed in the test failure output
+                Xml.newFastSerializer()
+                    .apply {
+                        setOutput(it, StandardCharsets.UTF_8.name())
+                        startDocument(null, true)
+                        setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true)
+                    }
+                    .apply(block)
+                    .endDocument()
+            }
+        }
+
+        internal fun <T> File.readXml(block: (parser: TypedXmlPullParser) -> T) =
+            inputStream().use {
+                block(Xml.resolvePullParser(it))
+            }
+    }
+
+    @Rule
+    @JvmField
+    val tempFolder = TemporaryFolder()
+
+    @Test
+    fun writeAndReadBackNormal() {
+        val attached = DomainVerificationStateMap<DomainVerificationPkgState>().apply {
+            mockPkgState(0).let { put(it.packageName, it.id, it) }
+            mockPkgState(1).let { put(it.packageName, it.id, it) }
+        }
+        val pending = ArrayMap<String, DomainVerificationPkgState>().apply {
+            mockPkgState(2).let { put(it.packageName, it) }
+            mockPkgState(3).let { put(it.packageName, it) }
+        }
+        val restored = ArrayMap<String, DomainVerificationPkgState>().apply {
+            mockPkgState(4).let { put(it.packageName, it) }
+            mockPkgState(5).let { put(it.packageName, it) }
+        }
+
+        val file = tempFolder.newFile().writeXml {
+            DomainVerificationPersistence.writeToXml(it, attached, pending, restored)
+        }
+
+        val xml = file.readText()
+
+        val (readActive, readRestored) = file.readXml {
+            DomainVerificationPersistence.readFromXml(it)
+        }
+
+        assertWithMessage(xml).that(readActive.values)
+            .containsExactlyElementsIn(attached.values() + pending.values)
+        assertWithMessage(xml).that(readRestored.values).containsExactlyElementsIn(restored.values)
+    }
+
+    @Test
+    fun readMalformed() {
+        val stateZero = mockEmptyPkgState(0).apply {
+            stateMap["example.com"] = DomainVerificationManager.STATE_SUCCESS
+            stateMap["example.org"] = DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED
+
+            // A domain without a written state falls back to default
+            stateMap["missing-state.com"] = DomainVerificationManager.STATE_NO_RESPONSE
+
+            userSelectionStates[1] = DomainVerificationUserState(1).apply {
+                addHosts(setOf("example-user1.com", "example-user1.org"))
+                isDisallowLinkHandling = false
+            }
+        }
+        val stateOne = mockEmptyPkgState(1).apply {
+            // It's valid to have a user selection without any autoVerify domains
+            userSelectionStates[1] = DomainVerificationUserState(1).apply {
+                addHosts(setOf("example-user1.com", "example-user1.org"))
+                isDisallowLinkHandling = true
+            }
+        }
+
+        // Also valid to have neither autoVerify domains nor any active user states
+        val stateTwo = mockEmptyPkgState(2, hasAutoVerifyDomains = false)
+
+        // language=XML
+        val xml = """
+            <?xml?>
+            <domain-verifications>
+                <active>
+                    <package-state
+                        packageName="${stateZero.packageName}"
+                        id="${stateZero.id}"
+                        >
+                        <state>
+                            <domain name="duplicate-takes-last.com" state="1"/>
+                        </state>
+                    </package-state>
+                    <package-state
+                        packageName="${stateZero.packageName}"
+                        id="${stateZero.id}"
+                        hasAutoVerifyDomains="true"
+                        >
+                        <state>
+                            <domain name="example.com" state="${DomainVerificationManager.STATE_SUCCESS}"/>
+                            <domain name="example.org" state="${DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED}"/>
+                            <not-domain name="not-domain.com" state="1"/>
+                            <domain name="missing-state.com"/>
+                        </state>
+                        <user-states>
+                            <user-state userId="1" disallowLinkHandling="false">
+                                <enabled-hosts>
+                                    <host name="example-user1.com"/>
+                                    <not-host name="not-host.com"/>
+                                    <host/>
+                                </enabled-hosts>
+                                <enabled-hosts>
+                                    <host name="example-user1.org"/>
+                                </enabled-hosts>
+                                <enabled-hosts/>
+                            </user-state>
+                            <user-state>
+                                <enabled-hosts>
+                                    <host name="no-user-id.com"/>
+                                </enabled-hosts>
+                            </user-state>
+                        </user-states>
+                    </package-state>
+                </active>
+                <not-active/>
+                <restored>
+                    <package-state
+                        packageName="${stateOne.packageName}"
+                        id="${stateOne.id}"
+                        hasAutoVerifyDomains="true"
+                        >
+                        <state/>
+                        <user-states>
+                            <user-state userId="1" disallowLinkHandling="true">
+                                <enabled-hosts>
+                                    <host name="example-user1.com"/>
+                                    <host name="example-user1.org"/>
+                                </enabled-hosts>
+                            </user-state>
+                        </user-states>
+                    </package-state>
+                    <package-state packageName="${stateTwo.packageName}"/>
+                    <package-state id="${stateTwo.id}"/>
+                    <package-state
+                        packageName="${stateTwo.packageName}"
+                        id="${stateTwo.id}"
+                        hasAutoVerifyDomains="false"
+                        >
+                        <state/>
+                        <user-states/>
+                    </package-state>
+                </restore>
+                <not-restored/>
+            </domain-verifications>
+        """.trimIndent()
+
+        val (active, restored) = DomainVerificationPersistence
+            .readFromXml(Xml.resolvePullParser(xml.byteInputStream()))
+
+        assertThat(active.values).containsExactly(stateZero)
+        assertThat(restored.values).containsExactly(stateOne, stateTwo)
+    }
+
+    private fun mockEmptyPkgState(
+        id: Int,
+        hasAutoVerifyDomains: Boolean = true
+    ): DomainVerificationPkgState {
+        val pkgName = pkgName(id)
+        val domainSetId = UUID(0L, id.toLong())
+        return DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains)
+    }
+
+    private fun mockPkgState(id: Int) = mockEmptyPkgState(id).apply {
+        stateMap["$packageName.com"] = id
+        userSelectionStates[id] = DomainVerificationUserState(id).apply {
+            addHosts(setOf("$packageName-user.com"))
+            isDisallowLinkHandling = true
+        }
+    }
+
+    private fun pkgName(id: Int) = "${PKG_PREFIX}.pkg$id"
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
new file mode 100644
index 0000000..db541f6
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageManager
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationRequest
+import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationState
+import android.os.Bundle
+import android.os.UserHandle
+import android.util.ArraySet
+import com.android.server.DeviceIdleInternal
+import com.android.server.pm.verify.domain.DomainVerificationCollector
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.util.UUID
+
+@Suppress("DEPRECATION")
+class DomainVerificationProxyTest {
+
+    companion object {
+        private const val TEST_PKG_NAME_ONE = "com.test.pkg.one"
+        private const val TEST_PKG_NAME_TWO = "com.test.pkg.two"
+        private const val TEST_PKG_NAME_TARGET_ONE = "com.test.target.one"
+        private const val TEST_PKG_NAME_TARGET_TWO = "com.test.target.two"
+        private const val TEST_CALLING_UID_ACCEPT = 40
+        private const val TEST_CALLING_UID_REJECT = 41
+        private val TEST_UUID_ONE = UUID.fromString("f7fbb7dd-7b5f-4609-a95e-c6c7765fb9cd")
+        private val TEST_UUID_TWO = UUID.fromString("4a09b361-a967-43ac-9d18-07a385dff740")
+    }
+
+    private val componentOne = ComponentName(TEST_PKG_NAME_ONE, ".ReceiverOne")
+    private val componentTwo = ComponentName(TEST_PKG_NAME_TWO, ".ReceiverTwo")
+    private val componentThree = ComponentName(TEST_PKG_NAME_TWO, ".ReceiverThree")
+
+    private lateinit var context: Context
+    private lateinit var manager: DomainVerificationManagerInternal
+    private lateinit var collector: DomainVerificationCollector
+
+    // Must be declared as field to support generics
+    @Captor
+    lateinit var hostCaptor: ArgumentCaptor<Set<String>>
+
+    @Before
+    fun setUpMocks() {
+        MockitoAnnotations.initMocks(this)
+        context = mockThrowOnUnmocked {
+            whenever(sendBroadcastAsUser(any(), any(), any(), any<Bundle>()))
+            whenever(
+                enforceCallingOrSelfPermission(
+                    eq(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT),
+                    anyString()
+                )
+            )
+        }
+        manager = mockThrowOnUnmocked {
+            whenever(getDomainVerificationInfoId(any())) {
+                when (val pkgName = arguments[0] as String) {
+                    TEST_PKG_NAME_TARGET_ONE -> TEST_UUID_ONE
+                    TEST_PKG_NAME_TARGET_TWO -> TEST_UUID_TWO
+                    else -> throw IllegalArgumentException("Unexpected package name $pkgName")
+                }
+            }
+            whenever(getDomainVerificationInfo(anyString())) {
+                when (val pkgName = arguments[0] as String) {
+                    TEST_PKG_NAME_TARGET_ONE -> DomainVerificationInfo(
+                        TEST_UUID_ONE, pkgName, mapOf(
+                            "example1.com" to DomainVerificationManager.STATE_NO_RESPONSE,
+                            "example2.com" to DomainVerificationManager.STATE_NO_RESPONSE
+                        )
+                    )
+                    TEST_PKG_NAME_TARGET_TWO -> DomainVerificationInfo(
+                        TEST_UUID_TWO, pkgName, mapOf(
+                            "example3.com" to DomainVerificationManager.STATE_NO_RESPONSE,
+                            "example4.com" to DomainVerificationManager.STATE_NO_RESPONSE
+                        )
+                    )
+                    else -> throw IllegalArgumentException("Unexpected package name $pkgName")
+                }
+            }
+            whenever(setDomainVerificationStatusInternal(anyInt(), any(), any(), anyInt()))
+        }
+        collector = mockThrowOnUnmocked {
+            whenever(collectAutoVerifyDomains(any())) {
+                when (val pkgName = (arguments[0] as AndroidPackage).packageName) {
+                    TEST_PKG_NAME_TARGET_ONE -> ArraySet(setOf("example1.com", "example2.com"))
+                    TEST_PKG_NAME_TARGET_TWO -> ArraySet(setOf("example3.com", "example4.com"))
+                    else -> throw IllegalArgumentException("Unexpected package name $pkgName")
+                }
+            }
+        }
+    }
+
+    @Test
+    fun isCallerVerifierV1() {
+        val connection = mockConnection()
+        val proxyV1 = DomainVerificationProxy.makeProxy<Connection>(
+            componentOne, null, context,
+            manager, collector, connection
+        )
+
+        assertThat(proxyV1.isCallerVerifier(TEST_CALLING_UID_ACCEPT)).isTrue()
+        verify(connection).isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_ONE)
+        verifyNoMoreInteractions(connection)
+        clearInvocations(connection)
+
+        assertThat(proxyV1.isCallerVerifier(TEST_CALLING_UID_REJECT)).isFalse()
+        verify(connection).isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_ONE)
+        verifyNoMoreInteractions(connection)
+    }
+
+    @Test
+    fun isCallerVerifierV2() {
+        val connection = mockConnection()
+        val proxyV2 = DomainVerificationProxy.makeProxy<Connection>(
+            null, componentTwo, context,
+            manager, collector, connection
+        )
+
+        assertThat(proxyV2.isCallerVerifier(TEST_CALLING_UID_ACCEPT)).isTrue()
+        verify(connection).isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_TWO)
+        verifyNoMoreInteractions(connection)
+        clearInvocations(connection)
+
+        assertThat(proxyV2.isCallerVerifier(TEST_CALLING_UID_REJECT)).isFalse()
+        verify(connection).isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_TWO)
+        verifyNoMoreInteractions(connection)
+    }
+
+    @Test
+    fun isCallerVerifierBoth() {
+        val connection = mockConnection()
+        val proxyBoth = DomainVerificationProxy.makeProxy<Connection>(
+            componentTwo, componentThree,
+            context, manager, collector, connection
+        )
+
+        // The combined proxy should only ever call v2 when it succeeds
+        assertThat(proxyBoth.isCallerVerifier(TEST_CALLING_UID_ACCEPT)).isTrue()
+        verify(connection).isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_TWO)
+        verifyNoMoreInteractions(connection)
+        clearInvocations(connection)
+
+        val callingUidCaptor = ArgumentCaptor.forClass(Int::class.java)
+
+        // But will call both when v2 fails
+        assertThat(proxyBoth.isCallerVerifier(TEST_CALLING_UID_REJECT)).isFalse()
+        verify(connection, times(2))
+            .isCallerPackage(callingUidCaptor.capture(), eq(TEST_PKG_NAME_TWO))
+        verifyNoMoreInteractions(connection)
+
+        assertThat(callingUidCaptor.allValues.toSet()).containsExactly(TEST_CALLING_UID_REJECT)
+    }
+
+    @Test
+    fun differentPackagesResolvesOnlyV2() {
+        assertThat(DomainVerificationProxy.makeProxy<Connection>(
+            componentOne, componentTwo,
+            context, manager, collector, mockConnection()
+        )).isInstanceOf(DomainVerificationProxyV2::class.java)
+    }
+
+    private fun prepareProxyV1(): ProxyV1Setup {
+        val messages = mutableListOf<Pair<Int, Any?>>()
+        val connection = mockConnection {
+            whenever(schedule(anyInt(), any())) {
+                messages.add((arguments[0] as Int) to arguments[1])
+            }
+        }
+
+        val proxy = DomainVerificationProxy.makeProxy<Connection>(
+            componentOne,
+            null,
+            context,
+            manager,
+            collector,
+            connection
+        )
+        return ProxyV1Setup(messages, connection, proxy)
+    }
+
+    @Test
+    fun sendBroadcastForPackagesV1() {
+        val (messages, _, proxy) = prepareProxyV1()
+
+        proxy.sendBroadcastForPackages(setOf(TEST_PKG_NAME_TARGET_ONE, TEST_PKG_NAME_TARGET_TWO))
+        messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+
+        val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+
+        verify(context, times(2)).sendBroadcastAsUser(
+            intentCaptor.capture(), eq(UserHandle.SYSTEM), isNull(), any<Bundle>()
+        )
+        verifyNoMoreInteractions(context)
+
+        val intents = intentCaptor.allValues
+        assertThat(intents).hasSize(2)
+        intents.forEach {
+            assertThat(it.action).isEqualTo(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION)
+            assertThat(it.getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME))
+                .isEqualTo(IntentFilter.SCHEME_HTTPS)
+            assertThat(it.getIntExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID, -1))
+                .isNotEqualTo(-1)
+        }
+
+        intents[0].apply {
+            assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME))
+                .isEqualTo(TEST_PKG_NAME_TARGET_ONE)
+            assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS))
+                .isEqualTo("example1.com example2.com")
+        }
+
+        intents[1].apply {
+            assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME))
+                .isEqualTo(TEST_PKG_NAME_TARGET_TWO)
+            assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS))
+                .isEqualTo("example3.com example4.com")
+        }
+    }
+
+    private fun prepareProxyOnIntentFilterVerifiedV1(): Pair<ProxyV1Setup, Pair<Int, Int>> {
+        val (messages, connection, proxy) = prepareProxyV1()
+
+        proxy.sendBroadcastForPackages(setOf(TEST_PKG_NAME_TARGET_ONE, TEST_PKG_NAME_TARGET_TWO))
+        messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+        messages.clear()
+
+        val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+
+        verify(context, times(2)).sendBroadcastAsUser(
+            intentCaptor.capture(), eq(UserHandle.SYSTEM), isNull(), any<Bundle>()
+        )
+
+        val verificationIds = intentCaptor.allValues.map {
+            it.getIntExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID, -1)
+        }
+
+        assertThat(verificationIds).doesNotContain(-1)
+
+        return ProxyV1Setup(messages, connection, proxy) to
+                (verificationIds[0] to verificationIds[1])
+    }
+
+    @Test
+    fun proxyOnIntentFilterVerifiedFullSuccessV1() {
+        val setup = prepareProxyOnIntentFilterVerifiedV1()
+        val (messages, connection, proxy) = setup.first
+        val (idOne, idTwo) = setup.second
+
+        DomainVerificationProxyV1.queueLegacyVerifyResult(
+            context,
+            connection,
+            idOne,
+            PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS,
+            emptyList(),
+            TEST_CALLING_UID_ACCEPT
+        )
+
+        DomainVerificationProxyV1.queueLegacyVerifyResult(
+            context,
+            connection,
+            idTwo,
+            PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS,
+            emptyList(),
+            TEST_CALLING_UID_ACCEPT
+        )
+
+        assertThat(messages).hasSize(2)
+        messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+
+        val idCaptor = ArgumentCaptor.forClass(UUID::class.java)
+
+        @Suppress("UNCHECKED_CAST")
+        verify(manager, times(2)).setDomainVerificationStatusInternal(
+            eq(TEST_CALLING_UID_ACCEPT),
+            idCaptor.capture(),
+            hostCaptor.capture(),
+            eq(DomainVerificationManager.STATE_SUCCESS)
+        )
+
+        assertThat(idCaptor.allValues).containsExactly(TEST_UUID_ONE, TEST_UUID_TWO)
+
+        assertThat(hostCaptor.allValues.toSet()).containsExactly(
+            setOf("example1.com", "example2.com"),
+            setOf("example3.com", "example4.com")
+        )
+    }
+
+    @Test
+    fun proxyOnIntentFilterVerifiedPartialSuccessV1() {
+        val setup = prepareProxyOnIntentFilterVerifiedV1()
+        val (messages, connection, proxy) = setup.first
+        val (idOne, idTwo) = setup.second
+
+        DomainVerificationProxyV1.queueLegacyVerifyResult(
+            context,
+            connection,
+            idOne,
+            PackageManager.INTENT_FILTER_VERIFICATION_FAILURE,
+            listOf("example1.com"),
+            TEST_CALLING_UID_ACCEPT
+        )
+
+        DomainVerificationProxyV1.queueLegacyVerifyResult(
+            context,
+            connection,
+            idTwo,
+            PackageManager.INTENT_FILTER_VERIFICATION_FAILURE,
+            listOf("example3.com"),
+            TEST_CALLING_UID_ACCEPT
+        )
+
+        messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+
+        val idCaptor = ArgumentCaptor.forClass(UUID::class.java)
+        val stateCaptor = ArgumentCaptor.forClass(Int::class.java)
+
+        @Suppress("UNCHECKED_CAST")
+        verify(manager, times(4)).setDomainVerificationStatusInternal(
+            eq(TEST_CALLING_UID_ACCEPT),
+            idCaptor.capture(),
+            hostCaptor.capture(),
+            stateCaptor.capture()
+        )
+
+        assertThat(idCaptor.allValues)
+            .containsExactly(TEST_UUID_ONE, TEST_UUID_ONE, TEST_UUID_TWO, TEST_UUID_TWO)
+
+        val hostToStates: Map<Set<*>, Int> = hostCaptor.allValues.zip(stateCaptor.allValues).toMap()
+        assertThat(hostToStates).isEqualTo(mapOf(
+            setOf("example1.com") to DomainVerificationState.STATE_LEGACY_FAILURE,
+            setOf("example2.com") to DomainVerificationState.STATE_SUCCESS,
+            setOf("example3.com") to DomainVerificationState.STATE_LEGACY_FAILURE,
+            setOf("example4.com") to DomainVerificationState.STATE_SUCCESS,
+        ))
+    }
+
+    @Test
+    fun proxyOnIntentFilterVerifiedFailureV1() {
+        val setup = prepareProxyOnIntentFilterVerifiedV1()
+        val (messages, connection, proxy) = setup.first
+        val (idOne, idTwo) = setup.second
+
+        DomainVerificationProxyV1.queueLegacyVerifyResult(
+            context,
+            connection,
+            idOne,
+            PackageManager.INTENT_FILTER_VERIFICATION_FAILURE,
+            listOf("example1.com", "example2.com"),
+            TEST_CALLING_UID_ACCEPT
+        )
+
+        DomainVerificationProxyV1.queueLegacyVerifyResult(
+            context,
+            connection,
+            idTwo,
+            PackageManager.INTENT_FILTER_VERIFICATION_FAILURE,
+            listOf("example3.com", "example4.com"),
+            TEST_CALLING_UID_ACCEPT
+        )
+
+        messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+
+        val idCaptor = ArgumentCaptor.forClass(UUID::class.java)
+
+        @Suppress("UNCHECKED_CAST")
+        verify(manager, times(2)).setDomainVerificationStatusInternal(
+            eq(TEST_CALLING_UID_ACCEPT),
+            idCaptor.capture(),
+            hostCaptor.capture(),
+            eq(DomainVerificationState.STATE_LEGACY_FAILURE)
+        )
+
+        assertThat(idCaptor.allValues).containsExactly(TEST_UUID_ONE, TEST_UUID_TWO)
+
+        assertThat(hostCaptor.allValues.toSet()).containsExactly(
+            setOf("example1.com", "example2.com"),
+            setOf("example3.com", "example4.com")
+        )
+    }
+
+    @Test
+    fun sendBroadcastForPackagesV2() {
+        val componentTwo = ComponentName(TEST_PKG_NAME_TWO, ".ReceiverOne")
+        val messages = mutableListOf<Pair<Int, Any?>>()
+
+        val connection = mockConnection {
+            whenever(schedule(anyInt(), any())) {
+                messages.add((arguments[0] as Int) to arguments[1])
+            }
+        }
+
+        val proxy = DomainVerificationProxy.makeProxy<Connection>(
+            null,
+            componentTwo,
+            context,
+            manager,
+            collector,
+            connection
+        )
+
+        proxy.sendBroadcastForPackages(setOf(TEST_PKG_NAME_TARGET_ONE, TEST_PKG_NAME_TARGET_TWO))
+
+        messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+
+        val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+
+        verify(context).sendBroadcastAsUser(
+            intentCaptor.capture(), eq(UserHandle.SYSTEM), isNull(), any<Bundle>()
+        )
+        verifyNoMoreInteractions(context)
+
+        val intents = intentCaptor.allValues
+        assertThat(intents).hasSize(1)
+        intents.single().apply {
+            assertThat(this.action).isEqualTo(Intent.ACTION_DOMAINS_NEED_VERIFICATION)
+            val request: DomainVerificationRequest? =
+                getParcelableExtra(DomainVerificationManager.EXTRA_VERIFICATION_REQUEST)
+            assertThat(request?.packageNames).containsExactly(
+                TEST_PKG_NAME_TARGET_ONE,
+                TEST_PKG_NAME_TARGET_TWO
+            )
+        }
+    }
+
+    private fun mockConnection(block: Connection.() -> Unit = {}) =
+        mockThrowOnUnmocked<Connection> {
+            whenever(isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_ONE)) { true }
+            whenever(isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_TWO)) { true }
+            whenever(isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_ONE)) { false }
+            whenever(isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_TWO)) { false }
+            whenever(getPackage(anyString())) { mockPkg(arguments[0] as String) }
+            whenever(powerSaveTempWhitelistAppDuration) { 1000 }
+            whenever(deviceIdleInternal) {
+                mockThrowOnUnmocked<DeviceIdleInternal> {
+                    whenever(
+                        addPowerSaveTempWhitelistApp(
+                            anyInt(), anyString(), anyLong(), anyInt(),
+                            anyBoolean(), anyString()
+                        )
+                    )
+                }
+            }
+            block()
+        }
+
+    private fun mockPkg(pkgName: String): AndroidPackage {
+        return mockThrowOnUnmocked { whenever(packageName) { pkgName } }
+    }
+
+    private data class ProxyV1Setup(
+        val messages: MutableList<Pair<Int, Any?>>,
+        val connection: Connection,
+        val proxy: DomainVerificationProxy
+    )
+
+    interface Connection : DomainVerificationProxyV1.Connection,
+        DomainVerificationProxyV2.Connection
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
index a18632b..961fc18 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
@@ -232,8 +232,8 @@
         ai.packageName = packageName;
         ai.uid = uid;
         ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
-        app.pid = pid;
-        mAms.mPidsSelfLocked.doAddInternal(app);
+        app.setPid(pid);
+        mAms.mPidsSelfLocked.doAddInternal(app.getPid(), app);
         mPhantomInjector.addToProcess(uid, pid, pid);
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 9441ecf..22b2f7e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -798,7 +798,7 @@
         final int traceEnd = 8192;
         createRandomFile(traceFile, traceSize);
         assertEquals(traceSize, traceFile.length());
-        mAppExitInfoTracker.handleLogAnrTrace(app.pid, app.uid, app.getPackageList(),
+        mAppExitInfoTracker.handleLogAnrTrace(app.getPid(), app.uid, app.getPackageList(),
                 traceFile, traceStart, traceEnd);
 
         noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
@@ -991,16 +991,16 @@
         ApplicationInfo ai = new ApplicationInfo();
         ai.packageName = packageName;
         ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
-        app.pid = pid;
+        app.setPid(pid);
         app.info.uid = packageUid;
         if (definingUid != null) {
             final String dummyPackageName = "com.android.test";
             final String dummyClassName = ".Foo";
-            app.hostingRecord = HostingRecord.byAppZygote(new ComponentName(
-                    dummyPackageName, dummyClassName), "", definingUid);
+            app.setHostingRecord(HostingRecord.byAppZygote(new ComponentName(
+                    dummyPackageName, dummyClassName), "", definingUid));
         }
-        app.connectionGroup = connectionGroup;
-        app.setProcState = procState;
+        app.mServices.setConnectionGroup(connectionGroup);
+        app.mState.setSetProcState(procState);
         app.mProfile.setLastMemInfo(spy(new Debug.MemoryInfo()));
         app.mProfile.setLastPss(pss);
         app.mProfile.setLastRss(rss);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index c82db73..022fadc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -170,7 +170,7 @@
                 /* lruWeight= */1.0f);
 
         ProcessList list = new ProcessList();
-        ArrayList<ProcessRecord> processList = list.mLruProcesses;
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord lastUsed40MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
                 Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
         processList.add(lastUsed40MinutesAgo);
@@ -191,7 +191,7 @@
                 Duration.ofMinutes(30).toMillis(), 1024L, 20);
         processList.add(lastUsed30MinutesAgo);
 
-        mCacheOomRanker.reRankLruCachedApps(list);
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
         // First 5 ordered by least recently used first, then last processes position unchanged.
         assertThat(processList).containsExactly(lastUsed60MinutesAgo, lastUsed42MinutesAgo,
@@ -207,7 +207,7 @@
                 /* lruWeight= */ 0.0f);
 
         ProcessList list = new ProcessList();
-        ArrayList<ProcessRecord> processList = list.mLruProcesses;
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
                 Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
         processList.add(rss10k);
@@ -231,7 +231,7 @@
                 Duration.ofMinutes(30).toMillis(), 16 * 1024L, 20);
         processList.add(rss16k);
 
-        mCacheOomRanker.reRankLruCachedApps(list);
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
         // First 6 ordered by largest pss, then last processes position unchanged.
         assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
@@ -246,8 +246,8 @@
                 /* lruWeight= */ 0.0f);
 
         ProcessList list = new ProcessList();
-        list.mLruProcessServiceStart = 1;
-        ArrayList<ProcessRecord> processList = list.mLruProcesses;
+        list.setLruProcessServiceStartLSP(1);
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
                 Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
         processList.add(used1000);
@@ -268,7 +268,7 @@
                 Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
         processList.add(used200);
 
-        mCacheOomRanker.reRankLruCachedApps(list);
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
         // First 4 ordered by uses, then last processes position unchanged.
         assertThat(processList).containsExactly(used10, used20, used1000, used2000, used500,
@@ -283,7 +283,7 @@
                 /* lruWeight= */ 0.3f);
 
         ProcessList list = new ProcessList();
-        ArrayList<ProcessRecord> processList = list.mLruProcesses;
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord unknownAdj1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
                 Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
         processList.add(unknownAdj1);
@@ -304,7 +304,7 @@
         processList.add(systemAdj);
 
         // 6 Processes but only 3 in eligible for cache so no re-ranking.
-        mCacheOomRanker.reRankLruCachedApps(list);
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
         // All positions unchanged.
         assertThat(processList).containsExactly(unknownAdj1, unknownAdj2, unknownAdj3,
@@ -319,8 +319,8 @@
                 /* lruWeight= */ 0.0f);
 
         ProcessList list = new ProcessList();
-        list.mLruProcessServiceStart = 4;
-        ArrayList<ProcessRecord> processList = list.mLruProcesses;
+        list.setLruProcessServiceStartLSP(4);
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
                 Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
         processList.add(used1000);
@@ -340,7 +340,7 @@
                 Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
         processList.add(used200);
 
-        mCacheOomRanker.reRankLruCachedApps(list);
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
         // All positions unchanged.
         assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
@@ -378,17 +378,17 @@
         ApplicationInfo ai = new ApplicationInfo();
         ai.packageName = "a.package.name" + mNextPackageName++;
         ProcessRecord app = new ProcessRecord(mAms, ai, ai.packageName + ":process", mNextUid++);
-        app.pid = mNextPid++;
+        app.setPid(mNextPid++);
         app.info.uid = mNextPackageUid++;
         // Exact value does not mater, it can be any state for which compaction is allowed.
-        app.setProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-        app.setAdj = setAdj;
-        app.lastActivityTime = lastActivityTime;
+        app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+        app.mState.setSetAdj(setAdj);
+        app.setLastActivityTime(lastActivityTime);
         app.mProfile.setLastRss(lastRss);
-        app.setCached(false);
+        app.mState.setCached(false);
         for (int i = 0; i < returnedToCacheCount; ++i) {
-            app.setCached(false);
-            app.setCached(true);
+            app.mState.setCached(false);
+            app.mState.setCached(true);
         }
         return app;
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 96a44a4..d860326 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -134,11 +134,11 @@
         ApplicationInfo ai = new ApplicationInfo();
         ai.packageName = packageName;
         ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
-        app.pid = pid;
+        app.setPid(pid);
         app.info.uid = packageUid;
         // Exact value does not mater, it can be any state for which compaction is allowed.
-        app.setProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-        app.setAdj = 905;
+        app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+        app.mState.setSetAdj(905);
         return app;
     }
 
@@ -875,7 +875,8 @@
         mProcessDependencies.setRss(rssBefore2);
         mProcessDependencies.setRssAfterCompaction(rssAfter2);
         // This is to avoid throttle of compacting too soon.
-        processRecord.lastCompactTime = processRecord.lastCompactTime - 10_000;
+        processRecord.mOptRecord.setLastCompactTime(
+                processRecord.mOptRecord.getLastCompactTime() - 10_000);
         // WHEN we try to run compaction.
         mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
         waitForHandler();
@@ -890,7 +891,8 @@
         mProcessDependencies.setRss(rssBefore3);
         mProcessDependencies.setRssAfterCompaction(rssAfter3);
         // This is to avoid throttle of compacting too soon.
-        processRecord.lastCompactTime = processRecord.lastCompactTime - 10_000;
+        processRecord.mOptRecord.setLastCompactTime(
+                processRecord.mOptRecord.getLastCompactTime() - 10_000);
         // WHEN we try to run compaction
         mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
         waitForHandler();
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 7daf357..27825a4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -176,6 +176,8 @@
         setFieldValue(ActivityManagerService.class, sService, "mUserController",
                 mock(UserController.class));
         setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
+        setFieldValue(ActivityManagerService.class, sService, "mProcLock",
+                new ActivityManagerProcLock());
         setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
         doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr)
                 .enqueueProcessChangeItemLocked(anyInt(), anyInt());
@@ -207,8 +209,8 @@
     public void testUpdateOomAdj_DoOne_Persistent_TopUi_Sleeping() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.maxAdj = PERSISTENT_PROC_ADJ;
-        app.setHasTopUi(true);
+        app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        app.mState.setHasTopUi(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -222,8 +224,8 @@
     public void testUpdateOomAdj_DoOne_Persistent_TopUi_Awake() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.maxAdj = PERSISTENT_PROC_ADJ;
-        app.setHasTopUi(true);
+        app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        app.mState.setHasTopUi(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -236,7 +238,7 @@
     public void testUpdateOomAdj_DoOne_Persistent_TopApp() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.maxAdj = PERSISTENT_PROC_ADJ;
+        app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
         doReturn(app).when(sService).getTopApp();
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -266,7 +268,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState();
-        app.runningRemoteAnimation = true;
+        app.mState.setRunningRemoteAnimation(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
         doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
@@ -308,7 +310,7 @@
     public void testUpdateOomAdj_DoOne_ExecutingService() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.executingServices.add(mock(ServiceRecord.class));
+        app.mServices.startExecutingService(mock(ServiceRecord.class));
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -337,7 +339,7 @@
     public void testUpdateOomAdj_DoOne_CachedEmpty() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.setCurRawAdj(CACHED_APP_MIN_ADJ);
+        app.mState.setCurRawAdj(CACHED_APP_MIN_ADJ);
         doReturn(null).when(sService).getTopApp();
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -351,7 +353,6 @@
     public void testUpdateOomAdj_DoOne_VisibleActivities() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).hasActivities();
         doAnswer(answer(callback -> {
@@ -368,7 +369,6 @@
                 any(WindowProcessController.ComputeOomAdjCallback.class));
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
-        doCallRealMethod().when(app).getWindowProcessController();
 
         assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
     }
@@ -378,15 +378,14 @@
     public void testUpdateOomAdj_DoOne_RecentTasks() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).hasRecentTasks();
-        app.lastTopTime = SystemClock.uptimeMillis();
+        app.mState.setLastTopTime(SystemClock.uptimeMillis());
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
         doCallRealMethod().when(wpc).hasRecentTasks();
 
-        assertEquals(PROCESS_STATE_CACHED_RECENT, app.setProcState);
+        assertEquals(PROCESS_STATE_CACHED_RECENT, app.mState.getSetProcState());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -394,7 +393,7 @@
     public void testUpdateOomAdj_DoOne_FgServiceLocation() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
+        app.mServices.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -407,7 +406,7 @@
     public void testUpdateOomAdj_DoOne_FgService() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.setHasForegroundServices(true, 0);
+        app.mServices.setHasForegroundServices(true, 0);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -420,7 +419,7 @@
     public void testUpdateOomAdj_DoOne_OverlayUi() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.setHasOverlayUi(true);
+        app.mState.setHasOverlayUi(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -433,8 +432,8 @@
     public void testUpdateOomAdj_DoOne_PerceptibleRecent() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.setHasForegroundServices(true, 0);
-        app.lastTopTime = SystemClock.uptimeMillis();
+        app.mServices.setHasForegroundServices(true, 0);
+        app.mState.setLastTopTime(SystemClock.uptimeMillis());
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -447,7 +446,7 @@
     public void testUpdateOomAdj_DoOne_Toast() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.forcingToImportant = new Object();
+        app.mState.setForcingToImportant(new Object());
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -460,7 +459,6 @@
     public void testUpdateOomAdj_DoOne_HeavyWeight() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).isHeavyWeightProcess();
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -476,7 +474,6 @@
     public void testUpdateOomAdj_DoOne_HomeApp() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -490,7 +487,6 @@
     public void testUpdateOomAdj_DoOne_PreviousApp() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).isPreviousProcess();
         doReturn(true).when(wpc).hasActivities();
@@ -522,11 +518,11 @@
     public void testUpdateOomAdj_DoOne_ClientActivities() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(true).when(app).hasClientActivities();
+        app.mServices.setHasClientActivities(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.setProcState);
+        assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.mState.getSetProcState());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -534,11 +530,11 @@
     public void testUpdateOomAdj_DoOne_TreatLikeActivity() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.treatLikeActivity = true;
+        app.mServices.setTreatLikeActivity(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.setProcState);
+        assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -546,12 +542,12 @@
     public void testUpdateOomAdj_DoOne_ServiceB() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.serviceb = true;
+        app.mState.setServiceB(true);
         ServiceRecord s = mock(ServiceRecord.class);
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
         s.startRequested = true;
         s.lastActivity = SystemClock.uptimeMillis();
-        app.startService(s);
+        app.mServices.startService(s);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -563,7 +559,7 @@
     public void testUpdateOomAdj_DoOne_MaxAdj() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.maxAdj = PERCEPTIBLE_LOW_APP_ADJ;
+        app.mState.setMaxAdj(PERCEPTIBLE_LOW_APP_ADJ);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -576,14 +572,14 @@
     public void testUpdateOomAdj_DoOne_NonCachedToCached() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.setCached(false);
-        app.setCurRawAdj(SERVICE_ADJ);
+        app.mState.setCached(false);
+        app.mState.setCurRawAdj(SERVICE_ADJ);
         doReturn(null).when(sService).getTopApp();
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertTrue(ProcessList.CACHED_APP_MIN_ADJ <= app.setAdj);
-        assertTrue(ProcessList.CACHED_APP_MAX_ADJ >= app.setAdj);
+        assertTrue(ProcessList.CACHED_APP_MIN_ADJ <= app.mState.getSetAdj());
+        assertTrue(ProcessList.CACHED_APP_MAX_ADJ >= app.mState.getSetAdj());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -595,7 +591,7 @@
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
         s.startRequested = true;
         s.lastActivity = SystemClock.uptimeMillis();
-        app.startService(s);
+        app.mServices.startService(s);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -633,7 +629,7 @@
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.setProcState);
+        assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -653,8 +649,8 @@
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(FOREGROUND_APP_ADJ, app.setAdj);
-        assertEquals(SCHED_GROUP_TOP_APP_BOUND, app.setSchedGroup);
+        assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
+        assertEquals(SCHED_GROUP_TOP_APP_BOUND, app.mState.getSetSchedGroup());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -676,12 +672,12 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        client.treatLikeActivity = true;
+        client.mServices.setTreatLikeActivity(true);
         bindService(app, client, null, 0, mock(IBinder.class));
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PROCESS_STATE_CACHED_EMPTY, app.setProcState);
+        assertEquals(PROCESS_STATE_CACHED_EMPTY, app.mState.getSetProcState());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -689,7 +685,6 @@
     public void testUpdateOomAdj_DoOne_Service_AllowOomManagement() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(false).when(wpc).isHomeProcess();
         doReturn(true).when(wpc).isPreviousProcess();
@@ -703,7 +698,7 @@
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
         doReturn(null).when(sService).getTopApp();
 
-        assertEquals(PREVIOUS_APP_ADJ, app.setAdj);
+        assertEquals(PREVIOUS_APP_ADJ, app.mState.getSetAdj());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -714,8 +709,8 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
-        client.maxAdj = PERSISTENT_PROC_ADJ;
-        client.setHasTopUi(true);
+        client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        client.mState.setHasTopUi(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -731,11 +726,11 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, Context.BIND_IMPORTANT, mock(IBinder.class));
-        client.executingServices.add(mock(ServiceRecord.class));
+        client.mServices.startExecutingService(mock(ServiceRecord.class));
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(FOREGROUND_APP_ADJ, app.setAdj);
+        assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -763,11 +758,11 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
-        client.maxAdj = PERSISTENT_PROC_ADJ;
+        client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.setProcState);
+        assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.mState.getSetProcState());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -778,11 +773,11 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class));
-        client.maxAdj = PERSISTENT_PROC_ADJ;
+        client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.setProcState);
+        assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.mState.getSetProcState());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -793,11 +788,11 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, 0, mock(IBinder.class));
-        client.setHasForegroundServices(true, 0);
+        client.mServices.setHasForegroundServices(true, 0);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, app.setProcState);
+        assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, app.mState.getSetProcState());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -815,12 +810,12 @@
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
         doReturn(null).when(sService.mBackupTargets).get(anyInt());
 
-        assertEquals(BACKUP_APP_ADJ, app.setAdj);
+        assertEquals(BACKUP_APP_ADJ, app.mState.getSetAdj());
 
-        client.maxAdj = PERSISTENT_PROC_ADJ;
+        client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PERSISTENT_SERVICE_ADJ, app.setAdj);
+        assertEquals(PERSISTENT_SERVICE_ADJ, app.mState.getSetAdj());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -831,11 +826,11 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
-        client.runningRemoteAnimation = true;
+        client.mState.setRunningRemoteAnimation(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.setAdj);
+        assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.mState.getSetAdj());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -846,11 +841,11 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class));
-        client.runningRemoteAnimation = true;
+        client.mState.setRunningRemoteAnimation(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PERCEPTIBLE_APP_ADJ, app.setAdj);
+        assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -861,11 +856,11 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, 0, mock(IBinder.class));
-        client.setHasOverlayUi(true);
+        client.mState.setHasOverlayUi(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PERCEPTIBLE_APP_ADJ, app.setAdj);
+        assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -876,11 +871,11 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, 0, mock(IBinder.class));
-        client.runningRemoteAnimation = true;
+        client.mState.setRunningRemoteAnimation(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(VISIBLE_APP_ADJ, app.setAdj);
+        assertEquals(VISIBLE_APP_ADJ, app.mState.getSetAdj());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -891,11 +886,11 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, Context.BIND_IMPORTANT_BACKGROUND, mock(IBinder.class));
-        client.setHasOverlayUi(true);
+        client.mState.setHasOverlayUi(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.setProcState);
+        assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.mState.getSetProcState());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -917,7 +912,7 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindProvider(app, client, null, null, false);
-        client.treatLikeActivity = true;
+        client.mServices.setTreatLikeActivity(true);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -948,7 +943,7 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        client.setHasForegroundServices(true, 0);
+        client.mServices.setHasForegroundServices(true, 0);
         bindProvider(app, client, null, null, false);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -977,7 +972,7 @@
     public void testUpdateOomAdj_DoOne_Provider_Retention() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.lastProviderTime = SystemClock.uptimeMillis();
+        app.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -1017,7 +1012,7 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(app, client2, null, 0, mock(IBinder.class));
-        client2.setHasForegroundServices(true, 0);
+        client2.mServices.setHasForegroundServices(true, 0);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -1036,7 +1031,7 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, 0, mock(IBinder.class));
-        client2.setHasForegroundServices(true, 0);
+        client2.mServices.setHasForegroundServices(true, 0);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -1055,9 +1050,9 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, 0, mock(IBinder.class));
-        client2.setHasForegroundServices(true, 0);
+        client2.mServices.setHasForegroundServices(true, 0);
         bindService(client2, app, null, 0, mock(IBinder.class));
-        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app);
         lru.add(client);
@@ -1072,13 +1067,13 @@
         assertProcStates(client2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
                 SCHED_GROUP_DEFAULT);
 
-        client2.setHasForegroundServices(false, 0);
+        client2.mServices.setHasForegroundServices(false, 0);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(client2, true, OomAdjuster.OOM_ADJ_REASON_NONE);
 
-        assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.setProcState);
-        assertEquals(PROCESS_STATE_CACHED_EMPTY, client.setProcState);
-        assertEquals(PROCESS_STATE_CACHED_EMPTY, app.setProcState);
+        assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.mState.getSetProcState());
+        assertEquals(PROCESS_STATE_CACHED_EMPTY, client.mState.getSetProcState());
+        assertEquals(PROCESS_STATE_CACHED_EMPTY, app.mState.getSetProcState());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -1093,8 +1088,8 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client2, client, null, 0, mock(IBinder.class));
-        client.setHasForegroundServices(true, 0);
-        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        client.mServices.setHasForegroundServices(true, 0);
+        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app);
         lru.add(client);
@@ -1121,11 +1116,11 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, 0, mock(IBinder.class));
-        client2.setHasForegroundServices(true, 0);
+        client2.mServices.setHasForegroundServices(true, 0);
         bindService(client2, app, null, 0, mock(IBinder.class));
         ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        client3.forcingToImportant = new Object();
+        client3.mState.setForcingToImportant(new Object());
         bindService(app, client3, null, 0, mock(IBinder.class));
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1146,12 +1141,11 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, 0, mock(IBinder.class));
         bindService(client2, app, null, 0, mock(IBinder.class));
-        doReturn(mock(WindowProcessController.class)).when(client2).getWindowProcessController();
         WindowProcessController wpc = client2.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        client3.forcingToImportant = new Object();
+        client3.mState.setForcingToImportant(new Object());
         bindService(app, client3, null, 0, mock(IBinder.class));
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1172,14 +1166,13 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, 0, mock(IBinder.class));
         bindService(client2, app, null, 0, mock(IBinder.class));
-        doReturn(mock(WindowProcessController.class)).when(client2).getWindowProcessController();
         WindowProcessController wpc = client2.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
         ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        client4.forcingToImportant = new Object();
+        client4.mState.setForcingToImportant(new Object());
         bindService(app, client4, null, 0, mock(IBinder.class));
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1200,16 +1193,15 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, 0, mock(IBinder.class));
         bindService(client2, app, null, 0, mock(IBinder.class));
-        doReturn(mock(WindowProcessController.class)).when(client2).getWindowProcessController();
         WindowProcessController wpc = client2.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        client3.forcingToImportant = new Object();
+        client3.mState.setForcingToImportant(new Object());
         bindService(app, client3, null, 0, mock(IBinder.class));
         ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        client4.setHasForegroundServices(true, 0);
+        client4.mServices.setHasForegroundServices(true, 0);
         bindService(app, client4, null, 0, mock(IBinder.class));
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1225,17 +1217,16 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        doReturn(mock(WindowProcessController.class)).when(client).getWindowProcessController();
         WindowProcessController wpc = client.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
         bindService(app, client, null, 0, mock(IBinder.class));
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(app, client2, null, 0, mock(IBinder.class));
-        client2.setHasForegroundServices(true, 0);
+        client2.mServices.setHasForegroundServices(true, 0);
         ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        client3.forcingToImportant = new Object();
+        client3.mState.setForcingToImportant(new Object());
         bindService(app, client3, null, 0, mock(IBinder.class));
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1255,7 +1246,7 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindProvider(client, client2, null, null, false);
-        client2.setHasForegroundServices(true, 0);
+        client2.mServices.setHasForegroundServices(true, 0);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -1274,7 +1265,7 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindProvider(client, client2, null, null, false);
-        client2.setHasForegroundServices(true, 0);
+        client2.mServices.setHasForegroundServices(true, 0);
         bindService(client2, app, null, 0, mock(IBinder.class));
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1294,7 +1285,7 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindProvider(client, client2, null, null, false);
-        client2.setHasForegroundServices(true, 0);
+        client2.mServices.setHasForegroundServices(true, 0);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -1313,7 +1304,7 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindProvider(client, client2, null, null, false);
-        client2.setHasForegroundServices(true, 0);
+        client2.mServices.setHasForegroundServices(true, 0);
         bindProvider(client2, app, null, null, false);
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1327,11 +1318,11 @@
     public void testUpdateOomAdj_DoAll_Unbound() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.forcingToImportant = new Object();
+        app.mState.setForcingToImportant(new Object());
         ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        app2.setHasForegroundServices(true, 0);
-        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        app2.mServices.setHasForegroundServices(true, 0);
+        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app);
         lru.add(app2);
@@ -1350,12 +1341,12 @@
     public void testUpdateOomAdj_DoAll_BoundFgService() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.forcingToImportant = new Object();
+        app.mState.setForcingToImportant(new Object());
         ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        app2.setHasForegroundServices(true, 0);
+        app2.mServices.setHasForegroundServices(true, 0);
         bindService(app, app2, null, 0, mock(IBinder.class));
-        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app);
         lru.add(app2);
@@ -1380,9 +1371,9 @@
         ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(app2, app3, null, 0, mock(IBinder.class));
-        app3.setHasForegroundServices(true, 0);
+        app3.mServices.setHasForegroundServices(true, 0);
         bindService(app3, app, null, 0, mock(IBinder.class));
-        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app);
         lru.add(app2);
@@ -1397,15 +1388,15 @@
                 SCHED_GROUP_DEFAULT);
         assertProcStates(app3, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
                 SCHED_GROUP_DEFAULT);
-        assertEquals("service", app.adjType);
-        assertEquals("service", app2.adjType);
-        assertEquals("fg-service", app3.adjType);
+        assertEquals("service", app.mState.getAdjType());
+        assertEquals("service", app2.mState.getAdjType());
+        assertEquals("fg-service", app3.mState.getAdjType());
         assertEquals(false, app.isCached());
         assertEquals(false, app2.isCached());
         assertEquals(false, app3.isCached());
-        assertEquals(false, app.empty);
-        assertEquals(false, app2.empty);
-        assertEquals(false, app3.empty);
+        assertEquals(false, app.mState.isEmpty());
+        assertEquals(false, app2.mState.isEmpty());
+        assertEquals(false, app3.mState.isEmpty());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -1420,18 +1411,17 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(app2, app3, null, 0, mock(IBinder.class));
         bindService(app3, app, null, 0, mock(IBinder.class));
-        doReturn(mock(WindowProcessController.class)).when(app3).getWindowProcessController();
         WindowProcessController wpc = app3.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        app4.setHasOverlayUi(true);
+        app4.mState.setHasOverlayUi(true);
         bindService(app, app4, s, 0, mock(IBinder.class));
         ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        app5.setHasForegroundServices(true, 0);
+        app5.mServices.setHasForegroundServices(true, 0);
         bindService(app, app5, s, 0, mock(IBinder.class));
-        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app);
         lru.add(app2);
@@ -1466,18 +1456,17 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(app2, app3, null, 0, mock(IBinder.class));
         bindService(app3, app, null, 0, mock(IBinder.class));
-        doReturn(mock(WindowProcessController.class)).when(app3).getWindowProcessController();
         WindowProcessController wpc = app3.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        app4.setHasOverlayUi(true);
+        app4.mState.setHasOverlayUi(true);
         bindService(app, app4, s, 0, mock(IBinder.class));
         ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        app5.setHasForegroundServices(true, 0);
+        app5.mServices.setHasForegroundServices(true, 0);
         bindService(app, app5, s, 0, mock(IBinder.class));
-        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app5);
         lru.add(app4);
@@ -1512,18 +1501,17 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(app2, app3, null, 0, mock(IBinder.class));
         bindService(app3, app, null, 0, mock(IBinder.class));
-        doReturn(mock(WindowProcessController.class)).when(app3).getWindowProcessController();
         WindowProcessController wpc = app3.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        app4.setHasOverlayUi(true);
+        app4.mState.setHasOverlayUi(true);
         bindService(app, app4, s, 0, mock(IBinder.class));
         ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        app5.setHasForegroundServices(true, 0);
+        app5.mServices.setHasForegroundServices(true, 0);
         bindService(app, app5, s, 0, mock(IBinder.class));
-        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app3);
         lru.add(app4);
@@ -1558,18 +1546,17 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindProvider(app2, app3, null, null, false);
         bindProvider(app3, app, null, null, false);
-        doReturn(mock(WindowProcessController.class)).when(app3).getWindowProcessController();
         WindowProcessController wpc = app3.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        app4.setHasOverlayUi(true);
+        app4.mState.setHasOverlayUi(true);
         bindProvider(app, app4, cr, null, false);
         ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        app5.setHasForegroundServices(true, 0);
+        app5.mServices.setHasForegroundServices(true, 0);
         bindProvider(app, app5, cr, null, false);
-        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app);
         lru.add(app2);
@@ -1612,11 +1599,11 @@
         s.app = app3;
         setFieldValue(ServiceRecord.class, s, "connections",
                 new ArrayMap<IBinder, ArrayList<ConnectionRecord>>());
-        app3.startService(s);
+        app3.mServices.startService(s);
         doCallRealMethod().when(s).getConnections();
         s.startRequested = true;
         s.lastActivity = now;
-        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app3);
         lru.add(app2);
@@ -1626,9 +1613,9 @@
         sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
         lru.clear();
 
-        assertEquals(SERVICE_B_ADJ, app3.setAdj);
-        assertEquals(SERVICE_ADJ, app2.setAdj);
-        assertEquals(SERVICE_ADJ, app.setAdj);
+        assertEquals(SERVICE_B_ADJ, app3.mState.getSetAdj());
+        assertEquals(SERVICE_ADJ, app2.mState.getSetAdj());
+        assertEquals(SERVICE_ADJ, app.mState.getSetAdj());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -1644,7 +1631,7 @@
         final int cachedAdj2 = cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
         doReturn(userOwner).when(sService.mUserController).getCurrentUserId();
 
-        final ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        final ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app2);
         lru.add(app);
@@ -1664,9 +1651,9 @@
         s.startRequested = true;
         s.lastActivity = now;
 
-        app.setCached(false);
-        app.startService(s);
-        app.hasShownUi = true;
+        app.mState.setCached(false);
+        app.mServices.startService(s);
+        app.mState.setHasShownUi(true);
 
         final ServiceInfo si2 = mock(ServiceInfo.class);
         si2.applicationInfo = mock(ApplicationInfo.class);
@@ -1677,9 +1664,9 @@
         s2.startRequested = true;
         s2.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
 
-        app2.setCached(false);
-        app2.startService(s2);
-        app2.hasShownUi = false;
+        app2.mState.setCached(false);
+        app2.mServices.startService(s2);
+        app2.mState.setHasShownUi(false);
 
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1687,28 +1674,28 @@
         assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services");
         assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj2, "cch-started-services");
 
-        app.setProcState = PROCESS_STATE_NONEXISTENT;
-        app.adjType = null;
-        app.setAdj = UNKNOWN_ADJ;
-        app.hasShownUi = false;
+        app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
+        app.mState.setAdjType(null);
+        app.mState.setSetAdj(UNKNOWN_ADJ);
+        app.mState.setHasShownUi(false);
         sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
 
         assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
 
-        app.setCached(false);
-        app.setProcState = PROCESS_STATE_NONEXISTENT;
-        app.adjType = null;
-        app.setAdj = UNKNOWN_ADJ;
+        app.mState.setCached(false);
+        app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
+        app.mState.setAdjType(null);
+        app.mState.setSetAdj(UNKNOWN_ADJ);
         s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
         sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
 
         assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
 
-        app.stopService(s);
-        app.setProcState = PROCESS_STATE_NONEXISTENT;
-        app.adjType = null;
-        app.setAdj = UNKNOWN_ADJ;
-        app.hasShownUi = true;
+        app.mServices.stopService(s);
+        app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
+        app.mState.setAdjType(null);
+        app.mState.setSetAdj(UNKNOWN_ADJ);
+        app.mState.setHasShownUi(true);
         sService.mConstants.KEEP_WARMING_SERVICES.add(cn);
         sService.mConstants.KEEP_WARMING_SERVICES.add(cn2);
         s = spy(new ServiceRecord(sService, cn, cn, null, 0, null,
@@ -1717,17 +1704,17 @@
         s.startRequested = true;
         s.lastActivity = now;
 
-        app.startService(s);
+        app.mServices.startService(s);
         sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
 
         assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
         assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
 
-        app.setCached(true);
-        app.setProcState = PROCESS_STATE_NONEXISTENT;
-        app.adjType = null;
-        app.setAdj = UNKNOWN_ADJ;
-        app.hasShownUi = false;
+        app.mState.setCached(true);
+        app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
+        app.mState.setAdjType(null);
+        app.mState.setSetAdj(UNKNOWN_ADJ);
+        app.mState.setHasShownUi(false);
         s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
         sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
 
@@ -1776,49 +1763,55 @@
         ai.longVersionCode = versionCode;
         ai.targetSdkVersion = targetSdkVersion;
         ProcessRecord app = new ProcessRecord(service, ai, processName, uid);
+        final ProcessStateRecord state = app.mState;
+        final ProcessServiceRecord services = app.mServices;
+        final ProcessReceiverRecord receivers = app.mReceivers;
         final ProcessProfileRecord profile = app.mProfile;
-        app.thread = mock(IApplicationThread.class);
-        app.lastActivityTime = lastActivityTime;
+        final ProcessProviderRecord providers = app.mProviders;
+        app.makeActive(mock(IApplicationThread.class), sService.mProcessStats);
+        app.setLastActivityTime(lastActivityTime);
+        app.setKilledByAm(killedByAm);
+        app.setIsolatedEntryPoint(isolatedEntryPoint);
+        setFieldValue(ProcessRecord.class, app, "mWindowProcessController",
+                mock(WindowProcessController.class));
         profile.setLastPssTime(lastPssTime);
         profile.setNextPssTime(nextPssTime);
         profile.setLastPss(lastPss);
-        app.maxAdj = maxAdj;
-        app.setRawAdj = setRawAdj;
-        app.curAdj = curAdj;
-        app.setAdj = setAdj;
-        app.setCurrentSchedulingGroup(curSchedGroup);
-        app.setSchedGroup = setSchedGroup;
-        app.setCurProcState(curProcState);
-        app.setReportedProcState(repProcState);
-        app.setCurRawProcState(curRawProcState);
-        app.setProcState = setProcState;
-        app.connectionGroup = connectionGroup;
-        app.connectionImportance = connectionImportance;
-        app.serviceb = serviceb;
-        app.setHasClientActivities(hasClientActivities);
-        app.setHasForegroundServices(hasForegroundServices, fgServiceTypes);
-        app.setHasClientActivities(hasForegroundActivities);
-        app.repForegroundActivities = repForegroundActivities;
-        app.systemNoUi = systemNoUi;
-        app.hasShownUi = hasShownUi;
-        app.setHasTopUi(hasTopUi);
-        app.setHasOverlayUi(hasOverlayUi);
-        app.runningRemoteAnimation = runningRemoteAnimation;
-        app.hasAboveClient = hasAboveClient;
-        app.treatLikeActivity = treatLikeActivity;
-        app.killedByAm = killedByAm;
-        app.forcingToImportant = forcingToImportant;
-        for (int i = 0; i < numOfCurReceivers; i++) {
-            app.curReceivers.add(mock(BroadcastRecord.class));
-        }
-        app.lastProviderTime = lastProviderTime;
-        app.lastTopTime = lastTopTime;
-        app.setCached(cached);
+        state.setMaxAdj(maxAdj);
+        state.setSetRawAdj(setRawAdj);
+        state.setCurAdj(curAdj);
+        state.setSetAdj(setAdj);
+        state.setCurrentSchedulingGroup(curSchedGroup);
+        state.setSetSchedGroup(setSchedGroup);
+        state.setCurProcState(curProcState);
+        state.setReportedProcState(repProcState);
+        state.setCurRawProcState(curRawProcState);
+        state.setSetProcState(setProcState);
+        state.setServiceB(serviceb);
+        state.setRepForegroundActivities(repForegroundActivities);
+        state.setHasForegroundActivities(hasForegroundActivities);
+        state.setSystemNoUi(systemNoUi);
+        state.setHasShownUi(hasShownUi);
+        state.setHasTopUi(hasTopUi);
+        state.setRunningRemoteAnimation(runningRemoteAnimation);
+        state.setHasOverlayUi(hasOverlayUi);
+        state.setCached(cached);
+        state.setLastTopTime(lastTopTime);
+        state.setForcingToImportant(forcingToImportant);
+        services.setConnectionGroup(connectionGroup);
+        services.setConnectionImportance(connectionImportance);
+        services.setHasClientActivities(hasClientActivities);
+        services.setHasForegroundServices(hasForegroundServices, fgServiceTypes);
+        services.setHasAboveClient(hasAboveClient);
+        services.setTreatLikeActivity(treatLikeActivity);
+        services.setExecServicesFg(execServicesFg);
         for (int i = 0; i < numOfExecutingServices; i++) {
-            app.executingServices.add(mock(ServiceRecord.class));
+            services.startExecutingService(mock(ServiceRecord.class));
         }
-        app.isolatedEntryPoint = isolatedEntryPoint;
-        app.execServicesFg = execServicesFg;
+        for (int i = 0; i < numOfCurReceivers; i++) {
+            receivers.addCurReceiver(mock(BroadcastRecord.class));
+        }
+        providers.setLastProviderTime(lastProviderTime);
         return app;
     }
 
@@ -1829,7 +1822,7 @@
             record.app = service;
             setFieldValue(ServiceRecord.class, record, "connections",
                     new ArrayMap<IBinder, ArrayList<ConnectionRecord>>());
-            service.startService(record);
+            service.mServices.startService(record);
             doCallRealMethod().when(record).getConnections();
         }
         AppBindRecord binding = new AppBindRecord(record, null, client);
@@ -1840,7 +1833,7 @@
         doCallRealMethod().when(record).addConnection(any(IBinder.class),
                 any(ConnectionRecord.class));
         record.addConnection(binder, cr);
-        client.connections.add(cr);
+        client.mServices.addConnection(cr);
         binding.connections.add(cr);
         doNothing().when(cr).trackProcState(anyInt(), anyInt(), anyLong());
         return record;
@@ -1850,7 +1843,7 @@
             ContentProviderRecord record, String name, boolean hasExternalProviders) {
         if (record == null) {
             record = mock(ContentProviderRecord.class);
-            publisher.pubProviders.put(name, record);
+            publisher.mProviders.installProvider(name, record);
             record.proc = publisher;
             setFieldValue(ContentProviderRecord.class, record, "connections",
                     new ArrayList<ContentProviderConnection>());
@@ -1859,22 +1852,24 @@
         ContentProviderConnection conn = spy(new ContentProviderConnection(record, client,
                 client.info.packageName));
         record.connections.add(conn);
-        client.conProviders.add(conn);
+        client.mProviders.addProviderConnection(conn);
         return record;
     }
 
     private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
             int expectedSchedGroup) {
-        assertEquals(expectedProcState, app.setProcState);
-        assertEquals(expectedAdj, app.setAdj);
-        assertEquals(expectedSchedGroup, app.setSchedGroup);
+        final ProcessStateRecord state = app.mState;
+        assertEquals(expectedProcState, state.getSetProcState());
+        assertEquals(expectedAdj, state.getSetAdj());
+        assertEquals(expectedSchedGroup, state.getSetSchedGroup());
     }
 
     private void assertProcStates(ProcessRecord app, boolean expectedCached,
             int expectedProcState, int expectedAdj, String expectedAdjType) {
-        assertEquals(expectedCached, app.isCached());
-        assertEquals(expectedProcState, app.setProcState);
-        assertEquals(expectedAdj, app.setAdj);
-        assertEquals(expectedAdjType, app.adjType);
+        final ProcessStateRecord state = app.mState;
+        assertEquals(expectedCached, state.isCached());
+        assertEquals(expectedProcState, state.getSetProcState());
+        assertEquals(expectedAdj, state.getSetAdj());
+        assertEquals(expectedAdjType, state.getAdjType());
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 84bfc9b..3b5cc88 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -96,6 +96,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Random;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -383,7 +384,7 @@
 
         LocationResult loc = createLocationResult(NAME, mRandom);
         mProvider.setProviderLocation(loc);
-        verify(listener).onLocationChanged(eq(loc), nullable(IRemoteCallback.class));
+        verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
     }
 
     @Test
@@ -406,13 +407,13 @@
 
         LocationResult loc = createLocationResult(NAME, mRandom);
         mProvider.setProviderLocation(loc);
-        verify(listener).onLocationChanged(eq(loc), nullable(IRemoteCallback.class));
+        verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
 
         mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
         verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, false);
         loc = createLocationResult(NAME, mRandom);
         mProvider.setProviderLocation(loc);
-        verify(listener, times(1)).onLocationChanged(any(LocationResult.class),
+        verify(listener, times(1)).onLocationChanged(any(List.class),
                 nullable(IRemoteCallback.class));
 
         mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
@@ -422,7 +423,7 @@
         verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, false);
         loc = createLocationResult(NAME, mRandom);
         mProvider.setProviderLocation(loc);
-        verify(listener, times(1)).onLocationChanged(any(LocationResult.class),
+        verify(listener, times(1)).onLocationChanged(any(List.class),
                 nullable(IRemoteCallback.class));
 
         mProvider.setAllowed(true);
@@ -430,7 +431,7 @@
 
         loc = createLocationResult(NAME, mRandom);
         mProvider.setProviderLocation(loc);
-        verify(listener).onLocationChanged(eq(loc), nullable(IRemoteCallback.class));
+        verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
     }
 
     @Test
@@ -447,7 +448,7 @@
 
         LocationResult loc = createLocationResult(NAME, mRandom);
         mProvider.setProviderLocation(loc);
-        verify(listener, timeout(TIMEOUT_MS).times(1)).onLocationChanged(eq(loc),
+        verify(listener, timeout(TIMEOUT_MS).times(1)).onLocationChanged(eq(loc.asList()),
                 nullable(IRemoteCallback.class));
     }
 
@@ -462,7 +463,7 @@
         mManager.unregisterLocationRequest(listener);
 
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
-        verify(listener, never()).onLocationChanged(any(LocationResult.class),
+        verify(listener, never()).onLocationChanged(any(List.class),
                 nullable(IRemoteCallback.class));
 
         mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
@@ -493,7 +494,7 @@
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
         mManager.unregisterLocationRequest(listener);
         blocker.countDown();
-        verify(listener, after(TIMEOUT_MS).never()).onLocationChanged(any(LocationResult.class),
+        verify(listener, after(TIMEOUT_MS).never()).onLocationChanged(any(List.class),
                 nullable(IRemoteCallback.class));
     }
 
@@ -513,7 +514,7 @@
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
 
-        verify(listener, times(5)).onLocationChanged(any(LocationResult.class),
+        verify(listener, times(5)).onLocationChanged(any(List.class),
                 nullable(IRemoteCallback.class));
     }
 
@@ -528,7 +529,7 @@
 
         mInjector.getAlarmHelper().incrementAlarmTime(5000);
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
-        verify(listener, never()).onLocationChanged(any(LocationResult.class),
+        verify(listener, never()).onLocationChanged(any(List.class),
                 nullable(IRemoteCallback.class));
     }
 
@@ -544,7 +545,7 @@
         Thread.sleep(25);
 
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
-        verify(listener, never()).onLocationChanged(any(LocationResult.class),
+        verify(listener, never()).onLocationChanged(any(List.class),
                 nullable(IRemoteCallback.class));
     }
 
@@ -561,7 +562,7 @@
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
 
         verify(listener, times(1)).onLocationChanged(
-                any(LocationResult.class), nullable(IRemoteCallback.class));
+                any(List.class), nullable(IRemoteCallback.class));
     }
 
     @Test
@@ -578,7 +579,7 @@
         mProvider.setProviderLocation(loc);
 
         verify(listener, times(1)).onLocationChanged(
-                any(LocationResult.class), nullable(IRemoteCallback.class));
+                any(List.class), nullable(IRemoteCallback.class));
     }
 
     @Test
@@ -592,7 +593,7 @@
 
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
 
-        verify(listener, never()).onLocationChanged(any(LocationResult.class),
+        verify(listener, never()).onLocationChanged(any(List.class),
                 nullable(IRemoteCallback.class));
     }
 
@@ -622,7 +623,7 @@
         verify(mWakeLock, never()).release();
 
         blocker.countDown();
-        verify(listener, timeout(TIMEOUT_MS)).onLocationChanged(any(LocationResult.class),
+        verify(listener, timeout(TIMEOUT_MS)).onLocationChanged(any(List.class),
                 nullable(IRemoteCallback.class));
         verify(mWakeLock).acquire(anyLong());
         verify(mWakeLock, timeout(TIMEOUT_MS)).release();
@@ -640,7 +641,7 @@
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
         verify(listener, times(1))
-                .onLocationChanged(any(LocationResult.class), nullable(IRemoteCallback.class));
+                .onLocationChanged(any(List.class), nullable(IRemoteCallback.class));
     }
 
     @Test
@@ -657,7 +658,7 @@
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
         verify(listener, times(1))
-                .onLocationChanged(any(LocationResult.class), nullable(IRemoteCallback.class));
+                .onLocationChanged(any(List.class), nullable(IRemoteCallback.class));
     }
 
     @Test
@@ -746,7 +747,7 @@
         mProvider.completeFlushes();
 
         InOrder inOrder = inOrder(listener);
-        inOrder.verify(listener).onLocationChanged(eq(loc), any(IRemoteCallback.class));
+        inOrder.verify(listener).onLocationChanged(eq(loc.asList()), any(IRemoteCallback.class));
         inOrder.verify(listener).onFlushComplete(99);
     }
 
@@ -838,7 +839,7 @@
                 .build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
-        verify(listener1).onLocationChanged(any(LocationResult.class),
+        verify(listener1).onLocationChanged(any(List.class),
                 nullable(IRemoteCallback.class));
 
         assertThat(mProvider.getRequest().isActive()).isFalse();
@@ -989,7 +990,7 @@
     private ILocationListener createMockLocationListener() {
         return spy(new ILocationListener.Stub() {
             @Override
-            public void onLocationChanged(LocationResult location,
+            public void onLocationChanged(List<Location> locations,
                     IRemoteCallback onCompleteCallback) {
                 if (onCompleteCallback != null) {
                     try {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
index 99846c5..07170da 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
@@ -185,8 +185,8 @@
 
     @Test
     public void testReportLocation() {
-        LocationResult realLocation = LocationResult.create(new Location("real"));
-        LocationResult mockLocation = LocationResult.create(new Location("mock"));
+        LocationResult realLocation = LocationResult.wrap(new Location("real"));
+        LocationResult mockLocation = LocationResult.wrap(new Location("mock"));
 
         mRealProvider.reportLocation(realLocation);
         assertThat(mListener.getNextLocationResult()).isEqualTo(realLocation);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index c522541..6e27b3a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -64,6 +64,8 @@
 import com.android.server.pm.parsing.pkg.PackageImpl
 import com.android.server.pm.parsing.pkg.ParsedPackage
 import com.android.server.pm.permission.PermissionManagerServiceInternal
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
+import com.android.server.testutils.TestHandler
 import com.android.server.testutils.mock
 import com.android.server.testutils.nullable
 import com.android.server.testutils.whenever
@@ -142,7 +144,7 @@
         }
         whenever(mocks.settings.addPackageLPw(nullable(), nullable(), nullable(), nullable(),
                 nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(),
-                nullable(), nullable(), nullable())) {
+                nullable(), nullable(), nullable(), nullable())) {
             val name: String = getArgument(0)
             val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name }
                     ?: return@whenever null
@@ -183,6 +185,8 @@
         val dexManager: DexManager = mock()
         val installer: Installer = mock()
         val displayMetrics: DisplayMetrics = mock()
+        val domainVerificationManagerInternal: DomainVerificationManagerInternal = mock()
+        val handler = TestHandler(null)
     }
 
     companion object {
@@ -258,6 +262,9 @@
         whenever(mocks.injector.userManagerInternal).thenReturn(mocks.userManagerInternal)
         whenever(mocks.injector.installer).thenReturn(mocks.installer)
         whenever(mocks.injector.displayMetrics).thenReturn(mocks.displayMetrics)
+        whenever(mocks.injector.domainVerificationManagerInternal)
+            .thenReturn(mocks.domainVerificationManagerInternal)
+        whenever(mocks.injector.handler) { mocks.handler }
         wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig)
         whenever(mocks.systemConfig.availableFeatures).thenReturn(DEFAULT_AVAILABLE_FEATURES_MAP)
         whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST)
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index 3b4699e..8c21a39 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -134,7 +134,7 @@
 
     private UidRecord addActiveUidRecord(int uid, long curProcStateSeq,
             long lastNetworkUpdatedProcStateSeq) {
-        final UidRecord record = new UidRecord(uid);
+        final UidRecord record = new UidRecord(uid, mAms);
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
         record.curProcStateSeq = curProcStateSeq;
         record.waitingForNetwork = true;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index e119d52..f314008 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -273,7 +273,7 @@
     }
 
     private UidRecord addUidRecord(int uid) {
-        final UidRecord uidRec = new UidRecord(uid);
+        final UidRecord uidRec = new UidRecord(uid, mAms);
         uidRec.waitingForNetwork = true;
         uidRec.hasInternetPermission = true;
         mAms.mProcessList.mActiveUids.put(uid, uidRec);
@@ -282,8 +282,9 @@
         info.packageName = "";
 
         final ProcessRecord appRec = new ProcessRecord(mAms, info, TAG, uid);
-        appRec.thread = mock(IApplicationThread.class);
-        mAms.mProcessList.mLruProcesses.add(appRec);
+        final ProcessStatsService tracker = new ProcessStatsService(mAms, mContext.getCacheDir());
+        appRec.makeActive(mock(IApplicationThread.class), tracker);
+        mAms.mProcessList.getLruProcessesLSP().add(appRec);
 
         return uidRec;
     }
@@ -295,23 +296,23 @@
         CustomThread thread = new CustomThread(uidRec.networkStateLock);
         thread.startAndWait("Unexpected state for " + uidRec);
 
-        uidRec.setProcState = prevState;
+        uidRec.setSetProcState(prevState);
         uidRec.setCurProcState(curState);
-        mAms.mProcessList.incrementProcStateSeqAndNotifyAppsLocked(mAms.mProcessList.mActiveUids);
+        mAms.mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(mAms.mProcessList.mActiveUids);
 
         // @SuppressWarnings("GuardedBy")
         assertEquals(expectedGlobalCounter, mAms.mProcessList.mProcStateSeqCounter);
         assertEquals(expectedCurProcStateSeq, uidRec.curProcStateSeq);
 
-        for (int i = mAms.mProcessList.getLruSizeLocked() - 1; i >= 0; --i) {
-            final ProcessRecord app = mAms.mProcessList.mLruProcesses.get(i);
+        for (int i = mAms.mProcessList.getLruSizeLOSP() - 1; i >= 0; --i) {
+            final ProcessRecord app = mAms.mProcessList.getLruProcessesLOSP().get(i);
             // AMS should notify apps only for block states other than NETWORK_STATE_NO_CHANGE.
-            if (app.uid == uidRec.uid && expectedBlockState == NETWORK_STATE_BLOCK) {
-                verify(app.thread).setNetworkBlockSeq(uidRec.curProcStateSeq);
+            if (app.uid == uidRec.getUid() && expectedBlockState == NETWORK_STATE_BLOCK) {
+                verify(app.getThread()).setNetworkBlockSeq(uidRec.curProcStateSeq);
             } else {
-                verifyZeroInteractions(app.thread);
+                verifyZeroInteractions(app.getThread());
             }
-            Mockito.reset(app.thread);
+            Mockito.reset(app.getThread());
         }
 
         if (expectNotify) {
@@ -446,55 +447,56 @@
 
     @Test
     public void testBlockStateForUid() {
-        final UidRecord uidRec = new UidRecord(TEST_UID);
+        final UidRecord uidRec = new UidRecord(TEST_UID, mAms);
         int expectedBlockState;
 
         final String errorTemplate = "Block state should be %s, prevState: %s, curState: %s";
         Function<Integer, String> errorMsg = (blockState) -> {
             return String.format(errorTemplate,
                     valueToString(ActivityManagerService.class, "NETWORK_STATE_", blockState),
-                    valueToString(ActivityManager.class, "PROCESS_STATE_", uidRec.setProcState),
+                    valueToString(ActivityManager.class, "PROCESS_STATE_",
+                        uidRec.getSetProcState()),
                     valueToString(ActivityManager.class, "PROCESS_STATE_", uidRec.getCurProcState())
             );
         };
 
         // No change in uid state
-        uidRec.setProcState = PROCESS_STATE_RECEIVER;
+        uidRec.setSetProcState(PROCESS_STATE_RECEIVER);
         uidRec.setCurProcState(PROCESS_STATE_RECEIVER);
         expectedBlockState = NETWORK_STATE_NO_CHANGE;
         assertEquals(errorMsg.apply(expectedBlockState),
                 expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
 
         // Foreground to foreground
-        uidRec.setProcState = PROCESS_STATE_FOREGROUND_SERVICE;
+        uidRec.setSetProcState(PROCESS_STATE_FOREGROUND_SERVICE);
         uidRec.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
         expectedBlockState = NETWORK_STATE_NO_CHANGE;
         assertEquals(errorMsg.apply(expectedBlockState),
                 expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
 
         // Background to background
-        uidRec.setProcState = PROCESS_STATE_CACHED_ACTIVITY;
+        uidRec.setSetProcState(PROCESS_STATE_CACHED_ACTIVITY);
         uidRec.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
         expectedBlockState = NETWORK_STATE_NO_CHANGE;
         assertEquals(errorMsg.apply(expectedBlockState),
                 expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
 
         // Background to background
-        uidRec.setProcState = PROCESS_STATE_NONEXISTENT;
+        uidRec.setSetProcState(PROCESS_STATE_NONEXISTENT);
         uidRec.setCurProcState(PROCESS_STATE_CACHED_ACTIVITY);
         expectedBlockState = NETWORK_STATE_NO_CHANGE;
         assertEquals(errorMsg.apply(expectedBlockState),
                 expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
 
         // Background to foreground
-        uidRec.setProcState = PROCESS_STATE_SERVICE;
+        uidRec.setSetProcState(PROCESS_STATE_SERVICE);
         uidRec.setCurProcState(PROCESS_STATE_FOREGROUND_SERVICE);
         expectedBlockState = NETWORK_STATE_BLOCK;
         assertEquals(errorMsg.apply(expectedBlockState),
                 expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
 
         // Foreground to background
-        uidRec.setProcState = PROCESS_STATE_TOP;
+        uidRec.setSetProcState(PROCESS_STATE_TOP);
         uidRec.setCurProcState(PROCESS_STATE_LAST_ACTIVITY);
         expectedBlockState = NETWORK_STATE_UNBLOCK;
         assertEquals(errorMsg.apply(expectedBlockState),
@@ -748,13 +750,13 @@
                         item.procState, validateUidRecord.getCurProcState());
                 assertEquals("processState: " + item.procState + " setProcState: "
                         + validateUidRecord.getCurProcState() + " should have been equal",
-                        item.procState, validateUidRecord.setProcState);
+                        item.procState, validateUidRecord.getSetProcState());
                 if (item.change == UidRecord.CHANGE_IDLE) {
                     assertTrue("UidRecord.idle should be updated to true for CHANGE_IDLE",
-                            validateUidRecord.idle);
+                            validateUidRecord.isIdle());
                 } else if (item.change == UidRecord.CHANGE_ACTIVE) {
                     assertFalse("UidRecord.idle should be updated to false for CHANGE_ACTIVE",
-                            validateUidRecord.idle);
+                            validateUidRecord.isIdle());
                 }
             }
         }
@@ -779,7 +781,7 @@
 
     @Test
     public void testEnqueueUidChangeLocked_procStateSeqUpdated() {
-        final UidRecord uidRecord = new UidRecord(TEST_UID);
+        final UidRecord uidRecord = new UidRecord(TEST_UID, mAms);
         uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
 
         // Verify with no pending changes for TEST_UID.
@@ -823,9 +825,9 @@
     @MediumTest
     @Test
     public void testEnqueueUidChangeLocked_dispatchUidsChanged() {
-        final UidRecord uidRecord = new UidRecord(TEST_UID);
+        final UidRecord uidRecord = new UidRecord(TEST_UID, mAms);
         final int expectedProcState = PROCESS_STATE_SERVICE;
-        uidRecord.setProcState = expectedProcState;
+        uidRecord.setSetProcState(expectedProcState);
         uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
 
         // Test with no pending uid records.
@@ -895,7 +897,7 @@
     private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
             long lastDispatchedProcStateSeq, long lastNetworkUpdatedProcStateSeq,
             final long procStateSeqToWait, boolean expectWait) throws Exception {
-        final UidRecord record = new UidRecord(Process.myUid());
+        final UidRecord record = new UidRecord(Process.myUid(), mAms);
         record.curProcStateSeq = curProcStateSeq;
         record.lastDispatchedProcStateSeq = lastDispatchedProcStateSeq;
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index 52c824a..432f6d6 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -33,6 +33,8 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -48,7 +50,26 @@
 
     @Before
     public void setUp() {
-        runWithDexmakerShareClassLoader(() -> mAnrApp = mock(ProcessRecord.class));
+        runWithDexmakerShareClassLoader(() -> {
+            mAnrApp = mock(ProcessRecord.class);
+            final ActivityManagerService service = mock(ActivityManagerService.class);
+            final ProcessErrorStateRecord errorState = mock(ProcessErrorStateRecord.class);
+            setFieldValue(ProcessErrorStateRecord.class, errorState, "mProcLock",
+                    new ActivityManagerProcLock());
+            setFieldValue(ProcessRecord.class, mAnrApp, "mErrorState", errorState);
+        });
+    }
+
+    private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
+        try {
+            Field field = clazz.getDeclaredField(fieldName);
+            field.setAccessible(true);
+            Field mfield = Field.class.getDeclaredField("accessFlags");
+            mfield.setAccessible(true);
+            mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
+            field.set(obj, val);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+        }
     }
 
     @Test
@@ -62,7 +83,7 @@
         mAnrHelper.appNotResponding(mAnrApp, activityShortComponentName, appInfo,
                 parentShortComponentName, parentProcess, aboveSystem, annotation);
 
-        verify(mAnrApp, timeout(TimeUnit.SECONDS.toMillis(5))).appNotResponding(
+        verify(mAnrApp.mErrorState, timeout(TimeUnit.SECONDS.toMillis(5))).appNotResponding(
                 eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName),
                 eq(parentProcess), eq(aboveSystem), eq(annotation), eq(false) /* onlyDumpSelf */);
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
index e44b0836..fdf5095 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
@@ -21,9 +21,13 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.content.Context;
+import android.hardware.power.stats.Channel;
 import android.hardware.power.stats.EnergyConsumer;
 import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyConsumerType;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.StateResidencyResult;
 import android.power.PowerStatsInternal;
 import android.util.SparseArray;
 import android.util.SparseLongArray;
@@ -146,6 +150,28 @@
             return future;
         }
 
+        @Override
+        public PowerEntity[] getPowerEntityInfo() {
+            return new PowerEntity[0];
+        }
+
+        @Override
+        public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
+                int[] powerEntityIds) {
+            return new CompletableFuture<>();
+        }
+
+        @Override
+        public Channel[] getEnergyMeterInfo() {
+            return new Channel[0];
+        }
+
+        @Override
+        public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
+                int[] channelIds) {
+            return new CompletableFuture<>();
+        }
+
         /**
          * Util method to add a new EnergyConsumer for testing
          *
diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
index 7355b80..638b1b4 100644
--- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
@@ -77,6 +77,8 @@
             sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper());
             sService.mAtmInternal = sService.mActivityTaskManager.getAtmInternal();
 
+            setFieldValue(ActivityManagerService.class, sService, "mProcLock",
+                    new ActivityManagerProcLock());
             sService.mConstants = new ActivityManagerConstants(sContext, sService,
                     sContext.getMainThreadHandler());
             final AppProfiler profiler = mock(AppProfiler.class);
@@ -124,7 +126,7 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStatePersistentUI() {
         final long elapsedTime = ZERO;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(ZERO, true, elapsedTime);
@@ -133,7 +135,7 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateTop() {
         final long elapsedTime = ZERO;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(ZERO, true, elapsedTime);
@@ -142,8 +144,8 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateTop_PreviousInteraction() {
         final long elapsedTime = ZERO;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
-        mProcessRecord.reportedInteraction = true;
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
+        mProcessRecord.mState.setReportedInteraction(true);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(ZERO, true, ZERO);
@@ -152,8 +154,8 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateTop_PastUsageInterval() {
         final long elapsedTime = 3 * USAGE_STATS_INTERACTION;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
-        mProcessRecord.reportedInteraction = true;
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
+        mProcessRecord.mState.setReportedInteraction(true);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(ZERO, true, elapsedTime);
@@ -162,7 +164,7 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateBoundTop() {
         final long elapsedTime = ZERO;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_TOP);
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_TOP);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(ZERO, true, elapsedTime);
@@ -171,7 +173,7 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateFGS() {
         final long elapsedTime = ZERO;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(elapsedTime, false, ZERO);
@@ -181,8 +183,8 @@
     public void testMaybeUpdateUsageStats_ProcStateFGS_ShortInteraction() {
         final long elapsedTime = ZERO;
         final long fgInteractionTime = 1000L;
-        mProcessRecord.setFgInteractionTime(fgInteractionTime);
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(fgInteractionTime, false, ZERO);
@@ -192,8 +194,8 @@
     public void testMaybeUpdateUsageStats_ProcStateFGS_LongInteraction() {
         final long elapsedTime = 2 * SERVICE_USAGE_INTERACTION;
         final long fgInteractionTime = 1000L;
-        mProcessRecord.setFgInteractionTime(fgInteractionTime);
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(fgInteractionTime, true, elapsedTime);
@@ -203,9 +205,9 @@
     public void testMaybeUpdateUsageStats_ProcStateFGS_PreviousLongInteraction() {
         final long elapsedTime = 2 * SERVICE_USAGE_INTERACTION;
         final long fgInteractionTime = 1000L;
-        mProcessRecord.setFgInteractionTime(fgInteractionTime);
-        mProcessRecord.reportedInteraction = true;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
+        mProcessRecord.mState.setReportedInteraction(true);
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(fgInteractionTime, true, ZERO);
@@ -214,7 +216,7 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateFGSLocation() {
         final long elapsedTime = ZERO;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(elapsedTime, false, ZERO);
@@ -223,7 +225,8 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateBFGS() {
         final long elapsedTime = ZERO;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+        mProcessRecord.mState.setCurProcState(
+                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(ZERO, true, elapsedTime);
@@ -232,7 +235,7 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateImportantFG() {
         final long elapsedTime = ZERO;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(ZERO, true, elapsedTime);
@@ -241,8 +244,8 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateImportantFG_PreviousInteraction() {
         final long elapsedTime = ZERO;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
-        mProcessRecord.reportedInteraction = true;
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+        mProcessRecord.mState.setReportedInteraction(true);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(ZERO, true, ZERO);
@@ -251,8 +254,8 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateImportantFG_PastUsageInterval() {
         final long elapsedTime = 3 * USAGE_STATS_INTERACTION;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
-        mProcessRecord.reportedInteraction = true;
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+        mProcessRecord.mState.setReportedInteraction(true);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(ZERO, true, elapsedTime);
@@ -261,7 +264,7 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateImportantBG() {
         final long elapsedTime = ZERO;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(ZERO, false, ZERO);
@@ -270,7 +273,7 @@
     @Test
     public void testMaybeUpdateUsageStats_ProcStateService() {
         final long elapsedTime = ZERO;
-        mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_SERVICE);
+        mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_SERVICE);
         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
 
         assertProcessRecordState(ZERO, false, ZERO);
@@ -279,10 +282,10 @@
     private void assertProcessRecordState(long fgInteractionTime, boolean reportedInteraction,
             long interactionEventTime) {
         assertEquals("Foreground interaction time was not updated correctly.",
-                fgInteractionTime, mProcessRecord.getFgInteractionTime());
+                fgInteractionTime, mProcessRecord.mState.getFgInteractionTime());
         assertEquals("Interaction was not updated correctly.",
-                reportedInteraction, mProcessRecord.reportedInteraction);
+                reportedInteraction, mProcessRecord.mState.hasReportedInteraction());
         assertEquals("Interaction event time was not updated correctly.",
-                interactionEventTime, mProcessRecord.getInteractionEventTime());
+                interactionEventTime, mProcessRecord.mState.getInteractionEventTime());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
index 263efa6..6538a17 100644
--- a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
@@ -44,7 +44,6 @@
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.util.Collections;
 
 /**
  * Build/Install/Run:
@@ -57,6 +56,7 @@
     private static ActivityManagerService sService;
 
     private ProcessRecord mProcessRecord;
+    private ProcessErrorStateRecord mProcessErrorState;
 
     @BeforeClass
     public static void setUpOnce() throws Exception {
@@ -72,6 +72,8 @@
             final AppProfiler profiler = mock(AppProfiler.class);
             setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
             setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
+            setFieldValue(ActivityManagerService.class, sService, "mProcLock",
+                    new ActivityManagerProcLock());
             final ProcessList processList = new ProcessList();
             setFieldValue(ActivityManagerService.class, sService, "mProcessList", processList);
         });
@@ -104,12 +106,12 @@
     public void setUpProcess() throws Exception {
         // Need to run with dexmaker share class loader to mock package private class.
         runWithDexmakerShareClassLoader(() -> {
-            mProcessRecord = spy(new ProcessRecord(sService, sContext.getApplicationInfo(),
-                    "name", 12345));
-            doNothing().when(mProcessRecord).startAppProblemLocked();
-            doReturn(false).when(mProcessRecord).isSilentAnr();
-            doReturn(false).when(mProcessRecord).isMonitorCpuUsage();
-            doReturn(Collections.emptyList()).when(mProcessRecord).getLruProcessList();
+            mProcessRecord = new ProcessRecord(sService, sContext.getApplicationInfo(),
+                    "name", 12345);
+            mProcessErrorState = spy(mProcessRecord.mErrorState);
+            doNothing().when(mProcessErrorState).startAppProblemLSP();
+            doReturn(false).when(mProcessErrorState).isSilentAnr();
+            doReturn(false).when(mProcessErrorState).isMonitorCpuUsage();
         });
     }
 
@@ -120,10 +122,10 @@
      */
     @Test
     public void testProcessDefaultAnrRelatedStatus() {
-        assertFalse(mProcessRecord.isNotResponding());
-        assertFalse(mProcessRecord.isCrashing());
-        assertFalse(mProcessRecord.killedByAm);
-        assertFalse(mProcessRecord.killed);
+        assertFalse(mProcessErrorState.isNotResponding());
+        assertFalse(mProcessErrorState.isCrashing());
+        assertFalse(mProcessRecord.isKilledByAm());
+        assertFalse(mProcessRecord.isKilled());
     }
 
     /**
@@ -131,12 +133,12 @@
      */
     @Test
     public void testAnrWhenCrash() {
-        mProcessRecord.setCrashing(true);
-        assertTrue(mProcessRecord.isCrashing());
-        appNotResponding(mProcessRecord, "Test ANR when crash");
-        assertFalse(mProcessRecord.isNotResponding());
-        assertFalse(mProcessRecord.killedByAm);
-        assertFalse(mProcessRecord.killed);
+        mProcessErrorState.setCrashing(true);
+        assertTrue(mProcessErrorState.isCrashing());
+        appNotResponding(mProcessErrorState, "Test ANR when crash");
+        assertFalse(mProcessErrorState.isNotResponding());
+        assertFalse(mProcessRecord.isKilledByAm());
+        assertFalse(mProcessRecord.isKilled());
     }
 
     /**
@@ -144,11 +146,11 @@
      */
     @Test
     public void testAnrWhenKilledByAm() {
-        mProcessRecord.killedByAm = true;
-        appNotResponding(mProcessRecord, "Test ANR when killed by AM");
-        assertFalse(mProcessRecord.isNotResponding());
-        assertFalse(mProcessRecord.isCrashing());
-        assertFalse(mProcessRecord.killed);
+        mProcessRecord.setKilledByAm(true);
+        appNotResponding(mProcessErrorState, "Test ANR when killed by AM");
+        assertFalse(mProcessErrorState.isNotResponding());
+        assertFalse(mProcessErrorState.isCrashing());
+        assertFalse(mProcessRecord.isKilled());
     }
 
     /**
@@ -156,11 +158,11 @@
      */
     @Test
     public void testAnrWhenKilled() {
-        mProcessRecord.killed = true;
-        appNotResponding(mProcessRecord, "Test ANR when killed");
-        assertFalse(mProcessRecord.isNotResponding());
-        assertFalse(mProcessRecord.isCrashing());
-        assertFalse(mProcessRecord.killedByAm);
+        mProcessRecord.setKilled(true);
+        appNotResponding(mProcessErrorState, "Test ANR when killed");
+        assertFalse(mProcessErrorState.isNotResponding());
+        assertFalse(mProcessErrorState.isCrashing());
+        assertFalse(mProcessRecord.isKilledByAm());
     }
 
     /**
@@ -169,11 +171,11 @@
      */
     @Test
     public void testNonSilentAnr() {
-        appNotResponding(mProcessRecord, "Test non-silent ANR");
-        assertTrue(mProcessRecord.isNotResponding());
-        assertFalse(mProcessRecord.isCrashing());
-        assertFalse(mProcessRecord.killedByAm);
-        assertFalse(mProcessRecord.killed);
+        appNotResponding(mProcessErrorState, "Test non-silent ANR");
+        assertTrue(mProcessErrorState.isNotResponding());
+        assertFalse(mProcessErrorState.isCrashing());
+        assertFalse(mProcessRecord.isKilledByAm());
+        assertFalse(mProcessRecord.isKilled());
     }
 
     /**
@@ -183,16 +185,17 @@
     @Test
     public void testSilentAnr() {
         // Silent Anr will run through even without a parent process, and directly killed by AM.
-        doReturn(true).when(mProcessRecord).isSilentAnr();
-        appNotResponding(mProcessRecord, "Test silent ANR");
-        assertTrue(mProcessRecord.isNotResponding());
-        assertFalse(mProcessRecord.isCrashing());
-        assertTrue(mProcessRecord.killedByAm);
-        assertTrue(mProcessRecord.killed);
+        doReturn(true).when(mProcessErrorState).isSilentAnr();
+        appNotResponding(mProcessErrorState, "Test silent ANR");
+        assertTrue(mProcessErrorState.isNotResponding());
+        assertFalse(mProcessErrorState.isCrashing());
+        assertTrue(mProcessRecord.isKilledByAm());
+        assertTrue(mProcessRecord.isKilled());
     }
 
-    private static void appNotResponding(ProcessRecord processRecord, String annotation) {
-        processRecord.appNotResponding(null /* activityShortComponentName */, null /* aInfo */,
+    private static void appNotResponding(ProcessErrorStateRecord processErrorState,
+            String annotation) {
+        processErrorState.appNotResponding(null /* activityShortComponentName */, null /* aInfo */,
                 null /* parentShortComponentName */, null /* parentProcess */,
                 false /* aboveSystem */, annotation, false /* onlyDumpSelf */);
     }
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 45bca68..1328b91 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -16,16 +16,18 @@
 
 package com.android.server.apphibernation;
 
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
 import static org.junit.Assert.assertTrue;
 import static org.mockito.AdditionalAnswers.returnsArgAt;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.intThat;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
-import static org.mockito.internal.verification.VerificationModeFactory.times;
 
 import android.app.IActivityManager;
 import android.content.BroadcastReceiver;
@@ -48,6 +50,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
@@ -76,18 +79,21 @@
     private IActivityManager mIActivityManager;
     @Mock
     private UserManager mUserManager;
+    @Mock
+    private HibernationStateDiskStore<UserLevelState> mHibernationStateDiskStore;
     @Captor
     private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
 
     @Before
     public void setUp() throws RemoteException {
+        // Share class loader to allow access to package-private classes
+        System.setProperty("dexmaker.share_classloader", "true");
         MockitoAnnotations.initMocks(this);
         doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
 
-        mAppHibernationService = new AppHibernationService(mContext, mIPackageManager,
-                mIActivityManager, mUserManager);
+        mAppHibernationService = new AppHibernationService(new MockInjector(mContext));
 
-        verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any());
+        verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
         mBroadcastReceiver = mReceiverCaptor.getValue();
 
         doReturn(mUserInfos).when(mUserManager).getUsers();
@@ -95,12 +101,19 @@
         doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
                 anyInt(), anyBoolean(), anyBoolean(), any(), any());
 
-        addUser(USER_ID_1);
+        List<PackageInfo> packages = new ArrayList<>();
+        packages.add(makePackageInfo(PACKAGE_NAME_1));
+        doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
+                intThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
         mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        UserInfo userInfo = addUser(USER_ID_1);
+        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
+        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
     }
 
     @Test
-    public void testSetHibernatingForUser_packageIsHibernating() throws RemoteException {
+    public void testSetHibernatingForUser_packageIsHibernating() {
         // WHEN we hibernate a package for a user
         mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
 
@@ -109,8 +122,7 @@
     }
 
     @Test
-    public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating()
-            throws RemoteException {
+    public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating() {
         // WHEN a new package is added and it is hibernated
         Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED,
                 Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
@@ -124,17 +136,12 @@
     }
 
     @Test
-    public void testSetHibernatingForUser_newUserAdded_packageIsHibernating()
+    public void testSetHibernatingForUser_newUserUnlocked_packageIsHibernating()
             throws RemoteException {
         // WHEN a new user is added and a package from the user is hibernated
-        List<PackageInfo> userPackages = new ArrayList<>();
-        userPackages.add(makePackageInfo(PACKAGE_NAME_1));
-        doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
-                .getInstalledPackages(anyInt(), eq(USER_ID_2));
-        Intent intent = new Intent(Intent.ACTION_USER_ADDED);
-        intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2);
-        mBroadcastReceiver.onReceive(mContext, intent);
-
+        UserInfo user2 = addUser(USER_ID_2);
+        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));
+        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
         mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
 
         // THEN the new user's package is hibernated
@@ -142,8 +149,7 @@
     }
 
     @Test
-    public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating()
-            throws RemoteException {
+    public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating() {
         // GIVEN a package is currently hibernated
         mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
 
@@ -168,25 +174,25 @@
     }
 
     /**
-     * Add a mock user with one package. Must be called before
-     * {@link AppHibernationService#onBootPhase(int)} to work properly.
+     * Add a mock user with one package.
      */
-    private void addUser(int userId) throws RemoteException {
-        addUser(userId, new String[]{PACKAGE_NAME_1});
+    private UserInfo addUser(int userId) throws RemoteException {
+        return addUser(userId, new String[]{PACKAGE_NAME_1});
     }
 
     /**
-     * Add a mock user with the packages specified. Must be called before
-     * {@link AppHibernationService#onBootPhase(int)} to work properly
+     * Add a mock user with the packages specified.
      */
-    private void addUser(int userId, String[] packageNames) throws RemoteException {
-        mUserInfos.add(new UserInfo(userId, "user_" + userId, 0 /* flags */));
+    private UserInfo addUser(int userId, String[] packageNames) throws RemoteException {
+        UserInfo userInfo = new UserInfo(userId, "user_" + userId, 0 /* flags */);
+        mUserInfos.add(userInfo);
         List<PackageInfo> userPackages = new ArrayList<>();
         for (String pkgName : packageNames) {
             userPackages.add(makePackageInfo(pkgName));
         }
         doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
-                .getInstalledPackages(anyInt(), eq(userId));
+                .getInstalledPackages(intThat(arg -> (arg & MATCH_ANY_USER) == 0), eq(userId));
+        return userInfo;
     }
 
     private static PackageInfo makePackageInfo(String packageName) {
@@ -194,4 +200,42 @@
         pkg.packageName = packageName;
         return pkg;
     }
+
+    private class MockInjector implements AppHibernationService.Injector {
+        private final Context mContext;
+
+        MockInjector(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public IActivityManager getActivityManager() {
+            return mIActivityManager;
+        }
+
+        @Override
+        public Context getContext() {
+            return mContext;
+        }
+
+        @Override
+        public IPackageManager getPackageManager() {
+            return mIPackageManager;
+        }
+
+        @Override
+        public UserManager getUserManager() {
+            return mUserManager;
+        }
+
+        @Override
+        public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
+            return Mockito.mock(HibernationStateDiskStore.class);
+        }
+
+        @Override
+        public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
+            return Mockito.mock(HibernationStateDiskStore.class);
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
new file mode 100644
index 0000000..59f3c35
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.FileUtils;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+
+@SmallTest
+public class HibernationStateDiskStoreTest {
+    private static final String STATES_FILE_NAME = "states";
+    private final MockScheduledExecutorService mMockScheduledExecutorService =
+            new MockScheduledExecutorService();
+
+    private File mFile;
+    private HibernationStateDiskStore<String> mHibernationStateDiskStore;
+
+
+    @Before
+    public void setUp() {
+        mFile = new File(InstrumentationRegistry.getContext().getCacheDir(), "test");
+        mHibernationStateDiskStore = new HibernationStateDiskStore<>(mFile,
+                new MockProtoReadWriter(), mMockScheduledExecutorService, STATES_FILE_NAME);
+    }
+
+    @After
+    public void tearDown() {
+        FileUtils.deleteContentsAndDir(mFile);
+    }
+
+    @Test
+    public void testScheduleWriteHibernationStates_writesDataThatCanBeRead() {
+        // GIVEN some data to be written
+        List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B"));
+
+        // WHEN the data is written
+        mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite);
+        mMockScheduledExecutorService.executeScheduledTask();
+
+        // THEN the read data is equal to what was written
+        List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates();
+        for (int i = 0; i < toWrite.size(); i++) {
+            assertEquals(toWrite.get(i), storedStrings.get(i));
+        }
+    }
+
+    @Test
+    public void testScheduleWriteHibernationStates_laterWritesOverwritePrevious() {
+        // GIVEN store has some data it is scheduled to write
+        mHibernationStateDiskStore.scheduleWriteHibernationStates(
+                new ArrayList<>(Arrays.asList("C", "D")));
+
+        // WHEN a write is scheduled with new data
+        List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B"));
+        mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite);
+        mMockScheduledExecutorService.executeScheduledTask();
+
+        // THEN the written data is the last scheduled data
+        List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates();
+        for (int i = 0; i < toWrite.size(); i++) {
+            assertEquals(toWrite.get(i), storedStrings.get(i));
+        }
+    }
+
+    /**
+     * Mock proto read / writer that just writes and reads a list of String data.
+     */
+    private final class MockProtoReadWriter implements ProtoReadWriter<List<String>> {
+        private static final long FIELD_ID = 1;
+
+        @Override
+        public void writeToProto(@NonNull ProtoOutputStream stream,
+                @NonNull List<String> data) {
+            for (int i = 0, size = data.size(); i < size; i++) {
+                stream.write(FIELD_ID, data.get(i));
+            }
+        }
+
+        @Nullable
+        @Override
+        public List<String> readFromProto(@NonNull ProtoInputStream stream)
+                throws IOException {
+            ArrayList<String> list = new ArrayList<>();
+            while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                list.add(stream.readString(FIELD_ID));
+            }
+            return list;
+        }
+    }
+
+    /**
+     * Mock scheduled executor service that has minimum implementation and can synchronously
+     * execute scheduled tasks.
+     */
+    private final class MockScheduledExecutorService implements ScheduledExecutorService {
+
+        Runnable mScheduledRunnable = null;
+
+        @Override
+        public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+            mScheduledRunnable = command;
+            return Mockito.mock(ScheduledFuture.class);
+        }
+
+        @Override
+        public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
+                long period, TimeUnit unit) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
+                long delay, TimeUnit unit) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void shutdown() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public List<Runnable> shutdownNow() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isShutdown() {
+            return false;
+        }
+
+        @Override
+        public boolean isTerminated() {
+            return false;
+        }
+
+        @Override
+        public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> Future<T> submit(Callable<T> task) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> Future<T> submit(Runnable task, T result) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Future<?> submit(Runnable task) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+                throws InterruptedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
+                TimeUnit unit) throws InterruptedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+                throws InterruptedException, ExecutionException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+                throws InterruptedException, ExecutionException, TimeoutException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void execute(Runnable command) {
+            throw new UnsupportedOperationException();
+        }
+
+        void executeScheduledTask() {
+            mScheduledRunnable.run();
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8c853cc..7597cbf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5616,43 +5616,52 @@
     public void testDisallowSharingIntoProfileSetRestriction() {
         when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
                 .thenReturn("com.android.managedprovisioning");
+        when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+                .thenReturn(UserHandle.USER_SYSTEM);
+        mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+        mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
         Bundle restriction = new Bundle();
         restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
 
-        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
-        RestrictionsListener listener = new RestrictionsListener(mContext);
+        RestrictionsListener listener = new RestrictionsListener(
+                mServiceContext, getServices().userManagerInternal, dpms);
         listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, restriction, new Bundle());
-        verifyDataSharingChangedBroadcast();
+
+        verifyDataSharingAppliedBroadcast();
     }
 
     @Test
     public void testDisallowSharingIntoProfileClearRestriction() {
         when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
                 .thenReturn("com.android.managedprovisioning");
+        when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+                .thenReturn(UserHandle.USER_SYSTEM);
+        mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+        mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
         Bundle restriction = new Bundle();
         restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
 
-        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
-        RestrictionsListener listener = new RestrictionsListener(mContext);
+        RestrictionsListener listener = new RestrictionsListener(
+                mServiceContext, getServices().userManagerInternal, dpms);
         listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), restriction);
-        verifyDataSharingChangedBroadcast();
+
+        verifyDataSharingAppliedBroadcast();
     }
 
     @Test
     public void testDisallowSharingIntoProfileUnchanged() {
-        RestrictionsListener listener = new RestrictionsListener(mContext);
+        RestrictionsListener listener = new RestrictionsListener(
+                mContext, getServices().userManagerInternal, dpms);
         listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), new Bundle());
         verify(mContext.spiedContext, never()).sendBroadcastAsUser(any(), any());
     }
 
-    private void verifyDataSharingChangedBroadcast() {
+    private void verifyDataSharingAppliedBroadcast() {
         Intent expectedIntent = new Intent(
-                DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
-        expectedIntent.setPackage("com.android.managedprovisioning");
-        expectedIntent.putExtra(Intent.EXTRA_USER_ID, CALLER_USER_HANDLE);
+                DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED);
         verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntent(expectedIntent),
-                MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+                MockUtils.checkUserHandle(CALLER_USER_HANDLE));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 95aac60..54da643 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -33,6 +33,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Optional;
+
 import javax.annotation.Nullable;
 
 /**
@@ -43,9 +45,9 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public final class DeviceStateManagerServiceTest {
-    private static final int DEFAULT_DEVICE_STATE = 0;
-    private static final int OTHER_DEVICE_STATE = 1;
-    private static final int UNSUPPORTED_DEVICE_STATE = 999;
+    private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT");
+    private static final DeviceState OTHER_DEVICE_STATE = new DeviceState(1, "OTHER");
+    private static final DeviceState UNSUPPORTED_DEVICE_STATE = new DeviceState(999, "UNSUPPORTED");
 
     private TestDeviceStatePolicy mPolicy;
     private TestDeviceStateProvider mProvider;
@@ -61,47 +63,53 @@
     @Test
     public void requestStateChange() {
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                DEFAULT_DEVICE_STATE.getIdentifier());
 
-        mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+        mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
         assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                OTHER_DEVICE_STATE.getIdentifier());
     }
 
     @Test
     public void requestStateChange_pendingState() {
         mPolicy.blockConfigure();
 
-        mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+        mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                OTHER_DEVICE_STATE.getIdentifier());
 
-        mProvider.notifyRequestState(DEFAULT_DEVICE_STATE);
+        mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                OTHER_DEVICE_STATE.getIdentifier());
 
         mPolicy.resumeConfigure();
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
     @Test
     public void requestStateChange_unsupportedState() {
-        mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE);
+        mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
     @Test
@@ -113,85 +121,83 @@
 
     @Test
     public void requestOverrideState() {
-        mService.setOverrideState(OTHER_DEVICE_STATE);
+        mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
         // Committed state changes as there is a requested override.
         assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                OTHER_DEVICE_STATE.getIdentifier());
 
         // Committed state is set back to the requested state once the override is cleared.
         mService.clearOverrideState();
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
     @Test
     public void requestOverrideState_unsupportedState() {
-        mService.setOverrideState(UNSUPPORTED_DEVICE_STATE);
+        mService.setOverrideState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
         // Committed state remains the same as the override state is unsupported.
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
     @Test
     public void supportedStatesChanged() {
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
 
-        mProvider.notifySupportedDeviceStates(new int []{ DEFAULT_DEVICE_STATE });
+        mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
 
         // The current committed and requests states do not change because the current state remains
         // supported.
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
-    }
-
-    @Test
-    public void supportedStatesChanged_invalidState() {
-        assertThrows(IllegalArgumentException.class, () -> {
-            mProvider.notifySupportedDeviceStates(new int []{ INVALID_DEVICE_STATE });
-        });
+        assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
     }
 
     @Test
     public void supportedStatesChanged_unsupportedRequestedState() {
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
 
-        mProvider.notifySupportedDeviceStates(new int []{ OTHER_DEVICE_STATE });
+        mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE });
 
         // The current requested state is cleared because it is no longer supported.
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), INVALID_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mService.getRequestedState(), Optional.empty());
 
-        mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+        mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
 
         assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
     }
 
     @Test
     public void supportedStatesChanged_unsupportedOverrideState() {
-        mService.setOverrideState(OTHER_DEVICE_STATE);
+        mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
         // Committed state changes as there is a requested override.
         assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                OTHER_DEVICE_STATE.getIdentifier());
 
-        mProvider.notifySupportedDeviceStates(new int []{ DEFAULT_DEVICE_STATE });
+        mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
 
         // Committed state is set back to the requested state as the override state is no longer
         // supported.
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
     @Test
@@ -199,23 +205,27 @@
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
 
-        mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+        mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
         assertNotNull(callback.getLastNotifiedValue());
-        assertEquals(callback.getLastNotifiedValue().intValue(), OTHER_DEVICE_STATE);
+        assertEquals(callback.getLastNotifiedValue().intValue(),
+                OTHER_DEVICE_STATE.getIdentifier());
 
-        mProvider.notifyRequestState(DEFAULT_DEVICE_STATE);
-        assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
+        mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
+        assertEquals(callback.getLastNotifiedValue().intValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier());
 
         mPolicy.blockConfigure();
-        mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+        mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
         // The callback should not have been notified of the state change as the policy is still
         // pending callback.
-        assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
+        assertEquals(callback.getLastNotifiedValue().intValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier());
 
         mPolicy.resumeConfigure();
         // Now that the policy is finished processing the callback should be notified of the state
         // change.
-        assertEquals(callback.getLastNotifiedValue().intValue(), OTHER_DEVICE_STATE);
+        assertEquals(callback.getLastNotifiedValue().intValue(),
+                OTHER_DEVICE_STATE.getIdentifier());
     }
 
     @Test
@@ -223,7 +233,8 @@
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
         assertNotNull(callback.getLastNotifiedValue());
-        assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
+        assertEquals(callback.getLastNotifiedValue().intValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
     private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
@@ -275,9 +286,8 @@
     }
 
     private static final class TestDeviceStateProvider implements DeviceStateProvider {
-        private int[] mSupportedDeviceStates = new int[]{ DEFAULT_DEVICE_STATE,
+        private DeviceState[] mSupportedDeviceStates = new DeviceState[]{ DEFAULT_DEVICE_STATE,
                 OTHER_DEVICE_STATE };
-        private int mCurrentDeviceState = DEFAULT_DEVICE_STATE;
         private Listener mListener;
 
         @Override
@@ -288,17 +298,16 @@
 
             mListener = listener;
             mListener.onSupportedDeviceStatesChanged(mSupportedDeviceStates);
-            mListener.onStateChanged(mCurrentDeviceState);
+            mListener.onStateChanged(mSupportedDeviceStates[0].getIdentifier());
         }
 
-        public void notifySupportedDeviceStates(int[] supportedDeviceStates) {
+        public void notifySupportedDeviceStates(DeviceState[] supportedDeviceStates) {
             mSupportedDeviceStates = supportedDeviceStates;
             mListener.onSupportedDeviceStatesChanged(supportedDeviceStates);
         }
 
-        public void notifyRequestState(int state) {
-            mCurrentDeviceState = state;
-            mListener.onStateChanged(state);
+        public void notifyRequestState(int identifier) {
+            mListener.onStateChanged(identifier);
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index ae966aa..f0b4f1b 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -227,6 +227,31 @@
     }
 
     @Test
+    public void testPhysicalStrategyRecalculateSplines() {
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
+                BACKLIGHT_RANGE);
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+        float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length];
+        for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) {
+            adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f;
+        }
+
+        // Default is unadjusted
+        assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
+        assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+
+        // When adjustment is turned on, adjustment array is used
+        strategy.recalculateSplines(true, adjustedNits50p);
+        assertEquals(1.3425f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
+        assertEquals(239.25f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+
+        // When adjustment is turned off, adjustment array is ignored
+        strategy.recalculateSplines(false, adjustedNits50p);
+        assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
+        assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+    }
+
+    @Test
     public void testDefaultStrategyIsPhysical() {
         Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
                 DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index e02a46af..d362791 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -335,6 +335,9 @@
         assertEquals(0.5, event.batteryLevel, FLOAT_DELTA);
         assertTrue(event.nightMode);
         assertEquals(3333, event.colorTemperature);
+        assertTrue(event.reduceBrightColors);
+        assertEquals(40, event.reduceBrightColorsStrength);
+        assertEquals(20f, event.reduceBrightColorsOffset, FLOAT_DELTA);
         assertEquals("a.package", event.packageName);
         assertEquals(0, event.userId);
         assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets);
@@ -561,6 +564,9 @@
         mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
         mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
 
+        mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
+        mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 40);
+
         startTracker(mTracker);
         mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
                 batteryChangeEvent(30, 100));
@@ -592,6 +598,9 @@
         assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
         assertTrue(event.nightMode);
         assertEquals(3339, event.colorTemperature);
+        assertTrue(event.reduceBrightColors);
+        assertEquals(40, event.reduceBrightColorsStrength);
+        assertEquals(20f, event.reduceBrightColorsOffset, FLOAT_DELTA);
         assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
         assertTrue(event.isUserSetBrightness);
         assertFalse(event.isDefaultBrightnessConfig);
@@ -606,6 +615,9 @@
         mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
         mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
 
+        mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
+        mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 40);
+
         startTracker(mTracker);
         mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
                 batteryChangeEvent(30, 100));
@@ -639,6 +651,7 @@
         assertEquals(brightness, event.brightness, FLOAT_DELTA);
         assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
         assertTrue(event.nightMode);
+        assertTrue(event.reduceBrightColors);
         assertEquals(3339, event.colorTemperature);
     }
 
@@ -661,6 +674,9 @@
         builder.setBatteryLevel(0.7f);
         builder.setNightMode(false);
         builder.setColorTemperature(345);
+        builder.setReduceBrightColors(false);
+        builder.setReduceBrightColorsStrength(40);
+        builder.setReduceBrightColorsOffset(20f);
         builder.setLastBrightness(50f);
         builder.setColorValues(new long[] {23, 34, 45}, 1000L);
         BrightnessChangeEvent event = builder.build();
@@ -684,6 +700,9 @@
         assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA);
         assertEquals(event.nightMode, event2.nightMode);
         assertEquals(event.colorTemperature, event2.colorTemperature);
+        assertEquals(event.reduceBrightColors, event2.reduceBrightColors);
+        assertEquals(event.reduceBrightColorsStrength, event2.reduceBrightColorsStrength);
+        assertEquals(event.reduceBrightColorsOffset, event2.reduceBrightColorsOffset, FLOAT_DELTA);
         assertEquals(event.lastBrightness, event2.lastBrightness, FLOAT_DELTA);
         assertArrayEquals(event.colorValueBuckets, event2.colorValueBuckets);
         assertEquals(event.colorSampleDuration, event2.colorSampleDuration);
@@ -1020,6 +1039,18 @@
         }
 
         @Override
+        public int getReduceBrightColorsStrength(Context context) {
+            return mSecureIntSettings.getOrDefault(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL,
+                    0);
+        }
+
+        @Override
+        public boolean isReduceBrightColorsActivated(Context context) {
+            return mSecureIntSettings.getOrDefault(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
+                    0) == 1;
+        }
+
+        @Override
         public DisplayedContentSample sampleColor(int noFramesToSample) {
             return new DisplayedContentSample(600L,
                     null,
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java b/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
new file mode 100644
index 0000000..35014dc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2021 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.display.color;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ReduceBrightColorsTintControllerTest {
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        final Resources mockResources = mock(Resources.class);
+        when(mockResources.getStringArray(
+                com.android.internal.R.array.config_reduceBrightColorsCoefficients))
+                .thenReturn(new String[]{"-0.000000000000001", "-0.955555555555554",
+                        "1.000000000000000"});
+        when(mockResources.getStringArray(
+                com.android.internal.R.array.config_reduceBrightColorsCoefficientsNonlinear))
+                .thenReturn(new String[]{"-0.4429953456", "-0.2434077725", "0.9809063061"});
+        mContext = mock(Context.class);
+        when(mContext.getResources()).thenReturn(mockResources);
+    }
+
+    @Test
+    public void setAndGetMatrix() {
+        final ReduceBrightColorsTintController tintController =
+                new ReduceBrightColorsTintController();
+        tintController.setUp(mContext, /* needsLinear= */ true);
+        tintController.setMatrix(50);
+        tintController.setActivated(true);
+        assertThat(tintController.getStrength()).isEqualTo(50);
+        assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+                .containsExactly(
+                        0.5222222f, 0f, 0f, 0f,
+                        0f, 0.5222222f, 0f, 0f,
+                        0f, 0f, 0.5222222f, 0f,
+                        0f, 0f, 0f, 1f)
+                .inOrder();
+    }
+
+    @Test
+    public void setAndGetMatrixClampToZero() {
+        final ReduceBrightColorsTintController tintController =
+                new ReduceBrightColorsTintController();
+        tintController.setUp(mContext, /* needsLinear= */ true);
+        tintController.setMatrix(-50);
+        tintController.setActivated(true);
+        assertThat(tintController.getStrength()).isEqualTo(0);
+        assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+                .containsExactly(
+                        1f, 0f, 0f, 0f,
+                        0f, 1f, 0f, 0f,
+                        0f, 0f, 1f, 0f,
+                        0f, 0f, 0f, 1f)
+                .inOrder();
+    }
+
+    @Test
+    public void setAndGetMatrixClampTo100() {
+        final ReduceBrightColorsTintController tintController =
+                new ReduceBrightColorsTintController();
+        tintController.setUp(mContext, /* needsLinear= */ true);
+        tintController.setMatrix(120);
+        tintController.setActivated(true);
+        assertThat(tintController.getStrength()).isEqualTo(100);
+        assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+                .containsExactly(
+                        0.04444444f, 0f, 0f, 0f,
+                        0f, 0.04444444f, 0f, 0f,
+                        0f, 0f, 0.04444444f, 0f,
+                        0f, 0f, 0f, 1f)
+                .inOrder();
+    }
+
+    @Test
+    public void returnsIdentityMatrixWhenNotActivated() {
+        final ReduceBrightColorsTintController tintController =
+                new ReduceBrightColorsTintController();
+        tintController.setUp(mContext, /* needsLinear= */ true);
+        tintController.setMatrix(50);
+        tintController.setActivated(true);
+        tintController.setActivated(false);
+        assertThat(tintController.getStrength()).isEqualTo(50);
+        assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+                .containsExactly(
+                        1f, 0f, 0f, 0f,
+                        0f, 1f, 0f, 0f,
+                        0f, 0f, 1f, 0f,
+                        0f, 0f, 0f, 1f)
+                .inOrder();
+    }
+
+    @Test
+    public void getAdjustedBrightnessZeroRbcStrengthFullBrightness() {
+        final ReduceBrightColorsTintController tintController =
+                new ReduceBrightColorsTintController();
+        tintController.setUp(mContext, /* needsLinear= */ true);
+        tintController.setMatrix(0);
+        assertThat(tintController.getAdjustedBrightness(450f)).isEqualTo(450f);
+    }
+
+    @Test
+    public void getAdjustedBrightnessFullRbcStrengthFullBrightness() {
+        final ReduceBrightColorsTintController tintController =
+                new ReduceBrightColorsTintController();
+        tintController.setUp(mContext, /* needsLinear= */ true);
+        tintController.setMatrix(100);
+        assertThat(tintController.getAdjustedBrightness(450f)).isEqualTo(19.999998f);
+    }
+
+    @Test
+    public void getAdjustedBrightnessZeroRbcStrengthLowBrightness() {
+        final ReduceBrightColorsTintController tintController =
+                new ReduceBrightColorsTintController();
+        tintController.setUp(mContext, /* needsLinear= */ true);
+        tintController.setMatrix(0);
+        assertThat(tintController.getAdjustedBrightness(2.2f)).isEqualTo(2.2f);
+    }
+
+    @Test
+    public void getAdjustedBrightnessFullRbcStrengthLowBrightness() {
+        final ReduceBrightColorsTintController tintController =
+                new ReduceBrightColorsTintController();
+        tintController.setUp(mContext, /* needsLinear= */ true);
+        tintController.setMatrix(100);
+        assertThat(tintController.getAdjustedBrightness(2.2f)).isEqualTo(0.09777778f);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
index c10cee9..86054e4 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
@@ -42,6 +42,8 @@
         long expectedModifiedDate = 1234567890;
         PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
         config.lastModifiedDate = expectedModifiedDate;
+        config.updatedFontDirs.add("~~abc");
+        config.updatedFontDirs.add("~~def");
 
         try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
             PersistentSystemFontConfig.writeToXml(baos, config);
@@ -54,6 +56,7 @@
                 PersistentSystemFontConfig.loadFromXml(bais, another);
 
                 assertThat(another.lastModifiedDate).isEqualTo(expectedModifiedDate);
+                assertThat(another.updatedFontDirs).containsExactly("~~abc", "~~def");
             }
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 8331031..cb83b0f 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -23,7 +23,9 @@
 
 import android.content.Context;
 import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontUpdateRequest;
 import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.Presubmit;
 import android.system.Os;
 
@@ -37,11 +39,12 @@
 import org.junit.runner.RunWith;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -150,13 +153,13 @@
         dirForPreparation.loadFontFileMap();
         assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
                 .isEqualTo(expectedModifiedDate);
-        installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
+        dirForPreparation.update(Arrays.asList(
+                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
         // Four font dirs are created.
         assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
-        //
         assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
                 .isNotEqualTo(expectedModifiedDate);
 
@@ -191,10 +194,11 @@
                 mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
                 mConfigFile);
         dirForPreparation.loadFontFileMap();
-        installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
+        dirForPreparation.update(Arrays.asList(
+                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
         // Four font dirs are created.
         assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
 
@@ -217,10 +221,11 @@
                 mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
                 mConfigFile);
         dirForPreparation.loadFontFileMap();
-        installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
+        dirForPreparation.update(Arrays.asList(
+                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
         // Four font dirs are created.
         assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
 
@@ -244,10 +249,11 @@
                 mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
                 mConfigFile);
         dirForPreparation.loadFontFileMap();
-        installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
-        installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
+        dirForPreparation.update(Arrays.asList(
+                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
         // Four font dirs are created.
         assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
 
@@ -282,6 +288,34 @@
     }
 
     @Test
+    public void construct_afterBatchFailure() throws Exception {
+        FakeFontFileParser parser = new FakeFontFileParser();
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dirForPreparation = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
+        dirForPreparation.loadFontFileMap();
+        dirForPreparation.update(
+                Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+        try {
+            dirForPreparation.update(Arrays.asList(
+                    newFontUpdateRequest("foo,2", GOOD_SIGNATURE),
+                    newFontUpdateRequest("bar,2", "Invalid signature")));
+            fail("Batch update with invalid signature should fail");
+        } catch (FontManagerService.SystemFontException e) {
+            // Expected
+        }
+
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
+        dir.loadFontFileMap();
+        // The state should be rolled back as a whole if one of the update requests fail.
+        assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
+        assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
+    }
+
+    @Test
     public void installFontFile() throws Exception {
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
@@ -290,7 +324,7 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        installFontFile(dir, "test,1", GOOD_SIGNATURE);
+        dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
         assertThat(dir.getFontFileMap()).containsKey("test.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1);
         File fontFile = dir.getFontFileMap().get("test.ttf");
@@ -308,9 +342,9 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        installFontFile(dir, "test,1", GOOD_SIGNATURE);
+        dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
         Map<String, File> mapBeforeUpgrade = dir.getFontFileMap();
-        installFontFile(dir, "test,2", GOOD_SIGNATURE);
+        dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
         assertThat(dir.getFontFileMap()).containsKey("test.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2);
         assertThat(mapBeforeUpgrade).containsKey("test.ttf");
@@ -327,9 +361,9 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        installFontFile(dir, "test,2", GOOD_SIGNATURE);
+        dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
         try {
-            installFontFile(dir, "test,1", GOOD_SIGNATURE);
+            dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
             fail("Expect IllegalArgumentException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
@@ -348,8 +382,26 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        installFontFile(dir, "foo,1", GOOD_SIGNATURE);
-        installFontFile(dir, "bar,2", GOOD_SIGNATURE);
+        dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("bar,2", GOOD_SIGNATURE)));
+        assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
+        assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
+        assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
+        assertThat(parser.getRevision(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(2);
+    }
+
+    @Test
+    public void installFontFile_batch() throws Exception {
+        FakeFontFileParser parser = new FakeFontFileParser();
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
+        dir.loadFontFileMap();
+
+        dir.update(Arrays.asList(
+                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar,2", GOOD_SIGNATURE)));
         assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
         assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -366,7 +418,8 @@
         dir.loadFontFileMap();
 
         try {
-            installFontFile(dir, "test,1", "Invalid signature");
+            dir.update(
+                    Collections.singletonList(newFontUpdateRequest("test,1", "Invalid signature")));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -386,7 +439,7 @@
         dir.loadFontFileMap();
 
         try {
-            installFontFile(dir, "test,1", GOOD_SIGNATURE);
+            dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
             fail("Expect IllegalArgumentException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
@@ -417,7 +470,8 @@
             dir.loadFontFileMap();
 
             try {
-                installFontFile(dir, "test,2", GOOD_SIGNATURE);
+                dir.update(
+                        Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
             } catch (FontManagerService.SystemFontException e) {
                 assertThat(e.getErrorCode())
                         .isEqualTo(FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG);
@@ -449,7 +503,7 @@
         dir.loadFontFileMap();
 
         try {
-            installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+            dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -477,7 +531,7 @@
         dir.loadFontFileMap();
 
         try {
-            installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+            dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -513,7 +567,7 @@
         dir.loadFontFileMap();
 
         try {
-            installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+            dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -522,13 +576,36 @@
         assertThat(dir.getFontFileMap()).isEmpty();
     }
 
-    private void installFontFile(UpdatableFontDir dir, String content, String signature)
+    @Test
+    public void installFontFile_batchFailure() throws Exception {
+        FakeFontFileParser parser = new FakeFontFileParser();
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
+        dir.loadFontFileMap();
+
+        dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+        try {
+            dir.update(Arrays.asList(
+                    newFontUpdateRequest("foo,2", GOOD_SIGNATURE),
+                    newFontUpdateRequest("bar,2", "Invalid signature")));
+            fail("Batch update with invalid signature should fail");
+        } catch (FontManagerService.SystemFontException e) {
+            // Expected
+        }
+        // The state should be rolled back as a whole if one of the update requests fail.
+        assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
+        assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
+    }
+
+    private FontUpdateRequest newFontUpdateRequest(String content, String signature)
             throws Exception {
         File file = File.createTempFile("font", "ttf", mCacheDir);
         FileUtils.stringToFile(file, content);
-        try (FileInputStream in = new FileInputStream(file)) {
-            dir.installFontFile(in.getFD(), signature.getBytes());
-        }
+        return new FontUpdateRequest(
+                ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY),
+                signature.getBytes());
     }
 
     private void writeConfig(PersistentSystemFontConfig.Config config,
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index 6208801..263cf48 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -77,18 +77,19 @@
             for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
                 if (mRandom.nextDouble() < stopRatio) {
                     running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
+                    mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
                 }
             }
             for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) {
                 if (mRandom.nextDouble() < stopRatio) {
                     running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1);
+                    mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
                 }
             }
         }
     }
 
-
-    private void startPendingJobs(Jobs jobs, int totalMax,
+    private void recount(Jobs jobs, int totalMax,
             @NonNull List<Pair<Integer, Integer>> minLimits,
             @NonNull List<Pair<Integer, Integer>> maxLimits) {
         mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig(
@@ -113,7 +114,9 @@
         }
 
         mWorkCountTracker.onCountDone();
+    }
 
+    private void startPendingJobs(Jobs jobs) {
         while ((jobs.pending.get(WORK_TYPE_TOP) > 0
                 && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
                 || (jobs.pending.get(WORK_TYPE_BG) > 0
@@ -151,7 +154,8 @@
             jobs.maybeFinishJobs(stopRatio);
             jobs.maybeEnqueueJobs(startRatio, fgJobRatio);
 
-            startPendingJobs(jobs, totalMax, minLimits, maxLimits);
+            recount(jobs, totalMax, minLimits, maxLimits);
+            startPendingJobs(jobs);
 
             int totalRunning = 0;
             for (int r = 0; r < jobs.running.size(); ++r) {
@@ -316,7 +320,8 @@
             jobs.pending.put(pend.first, pend.second);
         }
 
-        startPendingJobs(jobs, totalMax, minLimits, maxLimits);
+        recount(jobs, totalMax, minLimits, maxLimits);
+        startPendingJobs(jobs);
 
         for (Pair<Integer, Integer> run : resultRunning) {
             assertWithMessage("Incorrect running result for work type " + run.first)
@@ -421,4 +426,81 @@
                 /* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
                 /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
     }
+
+    /** Tests that the counter updates properly when jobs are stopped. */
+    @Test
+    public void testJobLifecycleLoop() {
+        final Jobs jobs = new Jobs();
+        jobs.pending.put(WORK_TYPE_TOP, 11);
+        jobs.pending.put(WORK_TYPE_BG, 10);
+
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 1));
+        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 5));
+
+        recount(jobs, totalMax, minLimits, maxLimits);
+
+        startPendingJobs(jobs);
+
+        assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(6);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(9);
+
+        // Stop all jobs
+        jobs.maybeFinishJobs(1);
+
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_TOP);
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+        startPendingJobs(jobs);
+
+        assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(1);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(8);
+
+        // Stop only a bg job and make sure the counter only allows another bg job to start.
+        jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) - 1);
+        mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
+
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_NONE);
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+        startPendingJobs(jobs);
+
+        assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(1);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(7);
+
+        // Stop only a top job and make sure the counter only allows another top job to start.
+        jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) - 1);
+        mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
+
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_TOP);
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_NONE);
+
+        startPendingJobs(jobs);
+
+        assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(7);
+
+        // Now that there are no more TOP jobs pending, BG should be able to start when TOP stops.
+        for (int i = jobs.running.get(WORK_TYPE_TOP); i > 0; --i) {
+            jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) - 1);
+            mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
+
+            assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+        }
+
+        startPendingJobs(jobs);
+
+        assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(0);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(5);
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(3);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index df19aeb..58ba907 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1829,11 +1829,11 @@
     }
 
     /**
-     * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+     * Exhaustively test checkUidNetworkingBlocked to output the expected results based on external
      * conditions.
      */
     @Test
-    public void testIsUidNetworkingBlocked() {
+    public void testCheckUidNetworkingBlocked() {
         final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
 
         // Metered network. Data saver on.
@@ -1877,17 +1877,16 @@
 
     private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
             ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
-        final NetworkPolicyManagerInternal npmi = LocalServices
-                .getService(NetworkPolicyManagerInternal.class);
 
         for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
             final boolean expectedResult = pair.first;
             final int rule = pair.second;
             assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
-                    expectedResult,
-                    npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+                    expectedResult, mService.checkUidNetworkingBlocked(UID_A, rule,
+                            metered, backgroundRestricted));
             assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
-                    npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+                    mService.checkUidNetworkingBlocked(SYSTEM_UID, rule, metered,
+                            backgroundRestricted));
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
new file mode 100644
index 0000000..764c504
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static com.android.server.devicepolicy.DpmTestUtils.assertRestrictions;
+import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.BundleUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest com.android.server.pm.BundleUtilsTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BundleUtilsTest {
+
+    @Test
+    public void testIsEmpty() {
+        assertThat(BundleUtils.isEmpty(null)).isTrue();
+        assertThat(BundleUtils.isEmpty(new Bundle())).isTrue();
+        assertThat(BundleUtils.isEmpty(newRestrictions("a"))).isFalse();
+    }
+
+    @Test
+    public void testClone() {
+        Bundle in = new Bundle();
+        Bundle out = BundleUtils.clone(in);
+        assertThat(in).isNotSameInstanceAs(out);
+        assertRestrictions(out, new Bundle());
+
+        out = BundleUtils.clone(null);
+        assertThat(out).isNotNull();
+        out.putBoolean("a", true); // Should not be Bundle.EMPTY.
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 90edaef..709b009 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -37,9 +37,10 @@
     private KeySetManagerService mKsms;
 
     public PackageSetting generateFakePackageSetting(String name) {
-        return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"),
-                "", "", "", "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/,
-                null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/);
+        return new PackageSettingBuilder()
+                .setName(name)
+                .setCodePath(new File(mContext.getCacheDir(), "fakeCodePath").getAbsolutePath())
+                .build();
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 4ce1bbc..558fb30 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -107,10 +107,15 @@
 
         // Create a real (non-null) PackageSetting and confirm that the removed
         // users are copied properly
-        setting = new PackageSetting("name", "realName", new File("codePath"),
-                "legacyNativeLibraryPathString", "primaryCpuAbiString", "secondaryCpuAbiString",
-                "cpuAbiOverrideString", 0, 0, 0, 0,
-                null, null, null);
+        setting = new PackageSettingBuilder()
+                .setName("name")
+                .setRealName("realName")
+                .setCodePath("codePath")
+                .setLegacyNativeLibraryPathString("legacyNativeLibraryPathString")
+                .setPrimaryCpuAbiString("primaryCpuAbiString")
+                .setSecondaryCpuAbiString("secondaryCpuAbiString")
+                .setCpuAbiOverrideString("cpuAbiOverrideString")
+                .build();
         pri.populateUsers(new int[] {
                 1, 2, 3, 4, 5
         }, setting);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 333ec929..59458e8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -33,10 +33,10 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
 import android.app.PropertyInvalidatedCache;
-import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
@@ -59,6 +59,7 @@
 
 import com.android.permission.persistence.RuntimePermissionsPersistence;
 import com.android.server.LocalServices;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.permission.LegacyPermissionDataProvider;
@@ -80,6 +81,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.UUID;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -94,10 +96,14 @@
     RuntimePermissionsPersistence mRuntimePermissionsPersistence;
     @Mock
     LegacyPermissionDataProvider mPermissionDataProvider;
+    @Mock
+    DomainVerificationManagerInternal mDomainVerificationManager;
 
     @Before
     public void initializeMocks() {
         MockitoAnnotations.initMocks(this);
+        when(mDomainVerificationManager.generateNewId())
+                .thenAnswer(invocation -> UUID.randomUUID());
     }
 
     @Before
@@ -112,10 +118,7 @@
             throws ReflectiveOperationException, IllegalAccessException {
         /* write out files and read */
         writeOldFiles();
-        final Context context = InstrumentationRegistry.getContext();
-        final Object lock = new Object();
-        Settings settings = new Settings(context.getFilesDir(),
-                mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+        Settings settings = makeSettings();
         assertThat(settings.readLPw(createFakeUsers()), is(true));
         verifyKeySetMetaData(settings);
     }
@@ -126,10 +129,7 @@
             throws ReflectiveOperationException, IllegalAccessException {
         // write out files and read
         writeOldFiles();
-        final Context context = InstrumentationRegistry.getContext();
-        final Object lock = new Object();
-        Settings settings = new Settings(context.getFilesDir(),
-                mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+        Settings settings = makeSettings();
         assertThat(settings.readLPw(createFakeUsers()), is(true));
 
         // write out, read back in and verify the same
@@ -142,10 +142,7 @@
     public void testSettingsReadOld() {
         // Write delegateshellthe package files and make sure they're parsed properly the first time
         writeOldFiles();
-        final Context context = InstrumentationRegistry.getContext();
-        final Object lock = new Object();
-        Settings settings = new Settings(context.getFilesDir(),
-                mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+        Settings settings = makeSettings();
         assertThat(settings.readLPw(createFakeUsers()), is(true));
         assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue()));
         assertThat(settings.getPackageLPr(PACKAGE_NAME_1), is(notNullValue()));
@@ -164,16 +161,12 @@
     public void testNewPackageRestrictionsFile() throws ReflectiveOperationException {
         // Write the package files and make sure they're parsed properly the first time
         writeOldFiles();
-        final Context context = InstrumentationRegistry.getContext();
-        final Object lock = new Object();
-        Settings settings = new Settings(context.getFilesDir(),
-                mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+        Settings settings = makeSettings();
         assertThat(settings.readLPw(createFakeUsers()), is(true));
         settings.writeLPr();
 
         // Create Settings again to make it read from the new files
-        settings = new Settings(context.getFilesDir(),
-                mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+        settings = makeSettings();
         assertThat(settings.readLPw(createFakeUsers()), is(true));
 
         PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_2);
@@ -200,10 +193,7 @@
     @Test
     public void testReadPackageRestrictions_noSuspendingPackage() {
         writePackageRestrictions_noSuspendingPackageXml(0);
-        final Object lock = new Object();
-        final Context context = InstrumentationRegistry.getTargetContext();
-        final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
-                lock);
+        Settings settingsUnderTest = makeSettings();
         final WatchableTester watcher =
                 new WatchableTester(settingsUnderTest, "noSuspendingPackage");
         watcher.register();
@@ -244,10 +234,7 @@
     @Test
     public void testReadPackageRestrictions_noSuspendParamsMap() {
         writePackageRestrictions_noSuspendParamsMapXml(0);
-        final Object lock = new Object();
-        final Context context = InstrumentationRegistry.getTargetContext();
-        final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
-                lock);
+        final Settings settingsUnderTest = makeSettings();
         final WatchableTester watcher =
                 new WatchableTester(settingsUnderTest, "noSuspendParamsMap");
         watcher.register();
@@ -281,9 +268,7 @@
 
     @Test
     public void testReadWritePackageRestrictions_suspendInfo() {
-        final Context context = InstrumentationRegistry.getTargetContext();
-        final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
-                new Object());
+        final Settings settingsUnderTest = makeSettings();
         final WatchableTester watcher = new WatchableTester(settingsUnderTest, "suspendInfo");
         watcher.register();
         final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
@@ -397,9 +382,7 @@
 
     @Test
     public void testReadWritePackageRestrictions_distractionFlags() {
-        final Context context = InstrumentationRegistry.getTargetContext();
-        final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
-                new Object());
+        final Settings settingsUnderTest = makeSettings();
         final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
         final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
         final PackageSetting ps3 = createPackageSetting(PACKAGE_NAME_3);
@@ -440,10 +423,7 @@
 
     @Test
     public void testWriteReadUsesStaticLibraries() {
-        final Context context = InstrumentationRegistry.getTargetContext();
-        final Object lock = new Object();
-        final Settings settingsUnderTest = new Settings(context.getFilesDir(),
-                mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+        final Settings settingsUnderTest = makeSettings();
         final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
         ps1.appId = Process.FIRST_APPLICATION_UID;
         ps1.pkg = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed())
@@ -516,10 +496,7 @@
     public void testEnableDisable() {
         // Write the package files and make sure they're parsed properly the first time
         writeOldFiles();
-        final Context context = InstrumentationRegistry.getContext();
-        final Object lock = new Object();
-        Settings settings = new Settings(context.getFilesDir(),
-                mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+        Settings settings = makeSettings();
         final WatchableTester watcher = new WatchableTester(settings, "testEnableDisable");
         watcher.register();
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -585,7 +562,8 @@
                 0,
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
-                null /*mimeGroups*/);
+                null /*mimeGroups*/,
+                UUID.randomUUID());
         final PackageSetting testPkgSetting01 = new PackageSetting(origPkgSetting01);
         verifySettingCopy(origPkgSetting01, testPkgSetting01);
     }
@@ -606,7 +584,8 @@
                 0,
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
-                null /*mimeGroups*/);
+                null /*mimeGroups*/,
+                UUID.randomUUID());
         final PackageSetting testPkgSetting01 = new PackageSetting(
                 PACKAGE_NAME /*pkgName*/,
                 REAL_PACKAGE_NAME /*realPkgName*/,
@@ -621,7 +600,8 @@
                 0,
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
-                null /*mimeGroups*/);
+                null /*mimeGroups*/,
+                UUID.randomUUID());
         testPkgSetting01.copyFrom(origPkgSetting01);
         verifySettingCopy(origPkgSetting01, testPkgSetting01);
     }
@@ -648,7 +628,8 @@
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
-                null /*mimeGroups*/);
+                null /*mimeGroups*/,
+                UUID.randomUUID());
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
         assertThat(testPkgSetting01.pkgFlags, is(0));
@@ -681,7 +662,8 @@
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
-                null /*mimeGroups*/);
+                null /*mimeGroups*/,
+                UUID.randomUUID());
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
         assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
@@ -698,12 +680,9 @@
     /** Update package; changing shared user throws exception */
     @Test
     public void testUpdatePackageSetting03() {
-        final Context context = InstrumentationRegistry.getContext();
-        final Object lock = new Object();
-        final Settings testSettings01 = new Settings(context.getFilesDir(),
-                mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+        Settings settings = makeSettings();
         final SharedUserSetting testUserSetting01 = createSharedUserSetting(
-                testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
+                settings, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
         final PackageSetting testPkgSetting01 =
                 createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
         try {
@@ -720,7 +699,8 @@
                     UserManagerService.getInstance(),
                     null /*usesStaticLibraries*/,
                     null /*usesStaticLibrariesVersions*/,
-                    null /*mimeGroups*/);
+                    null /*mimeGroups*/,
+                    UUID.randomUUID());
             fail("Expected a PackageManagerException");
         } catch (PackageManagerException expected) {
         }
@@ -752,7 +732,8 @@
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
-                null /*mimeGroups*/);
+                null /*mimeGroups*/,
+                UUID.randomUUID());
         assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
@@ -790,7 +771,8 @@
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
-                null /*mimeGroups*/);
+                null /*mimeGroups*/,
+                UUID.randomUUID());
         assertThat(testPkgSetting01.appId, is(0));
         assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
@@ -808,12 +790,9 @@
     /** Create PackageSetting for a shared user */
     @Test
     public void testCreateNewSetting03() {
-        final Context context = InstrumentationRegistry.getContext();
-        final Object lock = new Object();
-        final Settings testSettings01 = new Settings(context.getFilesDir(),
-                mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+        Settings settings = makeSettings();
         final SharedUserSetting testUserSetting01 = createSharedUserSetting(
-                testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
+                settings, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
         final PackageSetting testPkgSetting01 = Settings.createNewSetting(
                 PACKAGE_NAME,
                 null /*originalPkg*/,
@@ -834,7 +813,8 @@
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
-                null /*mimeGroups*/);
+                null /*mimeGroups*/,
+                UUID.randomUUID());
         assertThat(testPkgSetting01.appId, is(10064));
         assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
@@ -875,7 +855,8 @@
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
-                null /*mimeGroups*/);
+                null /*mimeGroups*/,
+                UUID.randomUUID());
         assertThat(testPkgSetting01.appId, is(10064));
         assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
@@ -934,6 +915,7 @@
         assertThat(origPkgSetting.getPathString(), is(testPkgSetting.getPathString()));
         assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString);
         assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString));
+        assertThat(origPkgSetting.getDomainSetId(), is(testPkgSetting.getDomainSetId()));
         assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime));
         assertSame(origPkgSetting.installSource, testPkgSetting.installSource);
         assertThat(origPkgSetting.installPermissionsFixed,
@@ -976,8 +958,6 @@
         assertNotSame(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
         // No equals() method for SparseArray object
         // assertThat(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
-        assertSame(origPkgSetting.verificationInfo, testPkgSetting.verificationInfo);
-        assertThat(origPkgSetting.verificationInfo, is(testPkgSetting.verificationInfo));
         assertThat(origPkgSetting.versionCode, is(testPkgSetting.versionCode));
         assertSame(origPkgSetting.volumeUuid, testPkgSetting.volumeUuid);
         assertThat(origPkgSetting.volumeUuid, is(testPkgSetting.volumeUuid));
@@ -1006,7 +986,8 @@
                 sharedUserId,
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
-                null /*mimeGroups*/);
+                null /*mimeGroups*/,
+                UUID.randomUUID());
     }
 
     private PackageSetting createPackageSetting(String packageName) {
@@ -1024,7 +1005,8 @@
                 0,
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
-                null /*mimeGroups*/);
+                null /*mimeGroups*/,
+                UUID.randomUUID());
     }
 
     private @NonNull List<UserInfo> createFakeUsers() {
@@ -1212,6 +1194,12 @@
         deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir());
     }
 
+    private Settings makeSettings() {
+        return new Settings(InstrumentationRegistry.getContext().getFilesDir(),
+                mRuntimePermissionsPersistence, mPermissionDataProvider,
+                mDomainVerificationManager, new Object());
+    }
+
     private void verifyKeySetMetaData(Settings settings)
             throws ReflectiveOperationException, IllegalAccessException {
         ArrayMap<String, PackageSetting> packages =
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 90c2982..e6a238a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -546,12 +546,17 @@
     }
 
     private static PackageSetting mockPkgSetting(AndroidPackage pkg) {
-        return new PackageSetting(pkg.getPackageName(), pkg.getRealPackage(),
-                new File(pkg.getPath()), null, pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(),
-                null, pkg.getVersionCode(),
-                PackageInfoUtils.appInfoFlags(pkg, null),
-                PackageInfoUtils.appInfoPrivateFlags(pkg, null),
-                pkg.getSharedUserLabel(), null, null, null);
+        return new PackageSettingBuilder()
+                .setName(pkg.getPackageName())
+                .setRealName(pkg.getRealPackage())
+                .setCodePath(pkg.getPath())
+                .setPrimaryCpuAbiString(pkg.getPrimaryCpuAbi())
+                .setSecondaryCpuAbiString(pkg.getSecondaryCpuAbi())
+                .setPVersionCode(pkg.getLongVersionCode())
+                .setPkgFlags(PackageInfoUtils.appInfoFlags(pkg, null))
+                .setPrivateFlags(PackageInfoUtils.appInfoPrivateFlags(pkg, null))
+                .setSharedUserId(pkg.getSharedUserLabel())
+                .build();
     }
 
     // NOTE: The equality assertions below are based on code autogenerated by IntelliJ.
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 84551c5..f75751b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -22,10 +22,10 @@
 import android.util.SparseArray;
 
 import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.PackageImpl;
 
 import java.io.File;
 import java.util.Map;
+import java.util.UUID;
 
 public class PackageSettingBuilder {
     private String mName;
@@ -48,6 +48,7 @@
     private long[] mUsesStaticLibrariesVersions;
     private Map<String, ArraySet<String>> mMimeGroups;
     private PackageParser.SigningDetails mSigningDetails;
+    private UUID mDomainSetId = UUID.randomUUID();
 
     public PackageSettingBuilder setPackage(AndroidPackage pkg) {
         this.mPkg = pkg;
@@ -163,12 +164,17 @@
         return this;
     }
 
+    public PackageSettingBuilder setDomainSetId(UUID domainSetId) {
+        mDomainSetId = domainSetId;
+        return this;
+    }
+
     public PackageSetting build() {
         final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
                 new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString,
                 mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags,
                 mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions,
-                mMimeGroups);
+                mMimeGroups, mDomainSetId);
         packageSetting.signatures = mSigningDetails != null
                 ? new PackageSignatures(mSigningDetails)
                 : new PackageSignatures();
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 90658055..27f3eec 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -465,10 +465,11 @@
     private static PackageSetting createPackageSetting() {
         // Generic PackageSetting object with values from a test app installed on a device to be
         // used to test the methods under the PackageSignatures signatures data member.
-        File appPath = new File("/data/app/app");
-        PackageSetting result = new PackageSetting("test.app", null, appPath,
-                "/data/app/app", null, null, null, 1, 940097092, 0, 0 /*userId*/, null, null,
-                null /*mimeGroups*/);
-        return result;
+        return new PackageSettingBuilder()
+                .setName("test.app")
+                .setCodePath("/data/app/app")
+                .setPVersionCode(1)
+                .setPkgFlags(940097092)
+                .build();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 1cfbad9..938e4cc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -53,18 +53,10 @@
         assertThat(testUserState.equals(oldUserState), is(true));
 
         oldUserState = new PackageUserState();
-        oldUserState.appLinkGeneration = 6;
-        assertThat(testUserState.equals(oldUserState), is(false));
-
-        oldUserState = new PackageUserState();
         oldUserState.ceDataInode = 4000L;
         assertThat(testUserState.equals(oldUserState), is(false));
 
         oldUserState = new PackageUserState();
-        oldUserState.domainVerificationStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
-        assertThat(testUserState.equals(oldUserState), is(false));
-
-        oldUserState = new PackageUserState();
         oldUserState.enabled = COMPONENT_ENABLED_STATE_ENABLED;
         assertThat(testUserState.equals(oldUserState), is(false));
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index d8c3979..b5add84 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -50,6 +50,7 @@
 import android.util.Pair;
 
 import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.PackageImpl;
@@ -91,6 +92,14 @@
         when(mMockInjector.getAbiHelper()).thenReturn(mMockPackageAbiHelper);
         when(mMockInjector.getUserManagerInternal()).thenReturn(mMockUserManager);
         when(mMockInjector.getCompatibility()).thenReturn(mMockCompatibility);
+
+        DomainVerificationManagerInternal domainVerificationManager =
+                mock(DomainVerificationManagerInternal.class);
+        when(domainVerificationManager.generateNewId())
+                .thenAnswer(invocation -> UUID.randomUUID());
+
+        when(mMockInjector.getDomainVerificationManagerInternal())
+                .thenReturn(domainVerificationManager);
     }
 
     @Before
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index ee30f68..cd98d44 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -72,11 +72,18 @@
     @Test
     public void testUserTypeBuilder_createUserType() {
         final Bundle restrictions = makeRestrictionsBundle("r1", "r2");
+        final Bundle systemSettings = makeSettingsBundle("s1", "s2");
+        final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2");
+        final List<DefaultCrossProfileIntentFilter> filters = List.of(
+                new DefaultCrossProfileIntentFilter.Builder(
+                DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                /* flags= */0,
+                /* letsPersonalDataIntoProfile= */false).build());
         final UserTypeDetails type = new UserTypeDetails.Builder()
                 .setName("a.name")
                 .setEnabled(true)
                 .setMaxAllowed(21)
-                .setBaseType(FLAG_FULL)
+                .setBaseType(FLAG_PROFILE)
                 .setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
                 .setBadgeLabels(23, 24, 25)
                 .setBadgeColors(26, 27)
@@ -86,20 +93,45 @@
                 .setLabel(31)
                 .setMaxAllowedPerParent(32)
                 .setDefaultRestrictions(restrictions)
+                .setDefaultSystemSettings(systemSettings)
+                .setDefaultSecureSettings(secureSettings)
+                .setDefaultCrossProfileIntentFilters(filters)
                 .createUserTypeDetails();
 
         assertEquals("a.name", type.getName());
         assertTrue(type.isEnabled());
         assertEquals(21, type.getMaxAllowed());
-        assertEquals(FLAG_FULL | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
+        assertEquals(FLAG_PROFILE | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
         assertEquals(28, type.getIconBadge());
         assertEquals(29, type.getBadgePlain());
         assertEquals(30, type.getBadgeNoBackground());
         assertEquals(31, type.getLabel());
         assertEquals(32, type.getMaxAllowedPerParent());
+
         assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions()));
         assertNotSame(restrictions, type.getDefaultRestrictions());
 
+        assertNotSame(systemSettings, type.getDefaultSystemSettings());
+        assertEquals(systemSettings.size(), type.getDefaultSystemSettings().size());
+        for (String key : systemSettings.keySet()) {
+            assertEquals(
+                    systemSettings.getString(key),
+                    type.getDefaultSystemSettings().getString(key));
+        }
+
+        assertNotSame(secureSettings, type.getDefaultSecureSettings());
+        assertEquals(secureSettings.size(), type.getDefaultSecureSettings().size());
+        for (String key : secureSettings.keySet()) {
+            assertEquals(
+                    secureSettings.getString(key),
+                    type.getDefaultSecureSettings().getString(key));
+        }
+
+        assertNotSame(filters, type.getDefaultCrossProfileIntentFilters());
+        assertEquals(filters.size(), type.getDefaultCrossProfileIntentFilters().size());
+        for (int i = 0; i < filters.size(); i++) {
+            assertEquals(filters.get(i), type.getDefaultCrossProfileIntentFilters().get(i));
+        }
 
         assertEquals(23, type.getBadgeLabel(0));
         assertEquals(24, type.getBadgeLabel(1));
@@ -135,6 +167,9 @@
         assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
         assertEquals(Resources.ID_NULL, type.getLabel());
         assertTrue(type.getDefaultRestrictions().isEmpty());
+        assertTrue(type.getDefaultSystemSettings().isEmpty());
+        assertTrue(type.getDefaultSecureSettings().isEmpty());
+        assertTrue(type.getDefaultCrossProfileIntentFilters().isEmpty());
 
         assertFalse(type.hasBadge());
     }
@@ -416,4 +451,13 @@
         }
         return bundle;
     }
+
+    /** Creates a Bundle of the given settings keys and puts true for the value. */
+    private static Bundle makeSettingsBundle(String ... settings) {
+        final Bundle bundle = new Bundle();
+        for (String setting : settings) {
+            bundle.putBoolean(setting, true);
+        }
+        return bundle;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index dc181a9..ddf0cd0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -48,23 +48,6 @@
         assertSame(in, UserRestrictionsUtils.nonNull(in));
     }
 
-    public void testIsEmpty() {
-        assertTrue(UserRestrictionsUtils.isEmpty(null));
-        assertTrue(UserRestrictionsUtils.isEmpty(new Bundle()));
-        assertFalse(UserRestrictionsUtils.isEmpty(newRestrictions("a")));
-    }
-
-    public void testClone() {
-        Bundle in = new Bundle();
-        Bundle out = UserRestrictionsUtils.clone(in);
-        assertNotSame(in, out);
-        assertRestrictions(out, new Bundle());
-
-        out = UserRestrictionsUtils.clone(null);
-        assertNotNull(out);
-        out.putBoolean("a", true); // Should not be Bundle.EMPTY.
-    }
-
     public void testMerge() {
         Bundle a = newRestrictions("a", "d");
         Bundle b = newRestrictions("b", "d", "e");
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 0bea584..4f36c8a 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -40,6 +40,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.server.LocalServices;
+import com.android.server.devicestate.DeviceState;
 import com.android.server.devicestate.DeviceStateProvider;
 
 import org.junit.After;
@@ -61,7 +62,8 @@
  * Run with <code>atest DeviceStateProviderImplTest</code>.
  */
 public final class DeviceStateProviderImplTest {
-    private final ArgumentCaptor<int[]> mIntArrayCaptor = ArgumentCaptor.forClass(int[].class);
+    private final ArgumentCaptor<DeviceState[]> mDeviceStateArrayCaptor = ArgumentCaptor.forClass(
+            DeviceState[].class);
     private final ArgumentCaptor<Integer> mIntegerCaptor = ArgumentCaptor.forClass(Integer.class);
 
     private Context mContext;
@@ -120,11 +122,12 @@
         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
         provider.setListener(listener);
 
-        verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
-        assertArrayEquals(new int[] { DEFAULT_DEVICE_STATE }, mIntArrayCaptor.getValue());
+        verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+        assertArrayEquals(new DeviceState[]{DEFAULT_DEVICE_STATE},
+                mDeviceStateArrayCaptor.getValue());
 
         verify(listener).onStateChanged(mIntegerCaptor.capture());
-        assertEquals(DEFAULT_DEVICE_STATE, mIntegerCaptor.getValue().intValue());
+        assertEquals(DEFAULT_DEVICE_STATE.getIdentifier(), mIntegerCaptor.getValue().intValue());
     }
 
     @Test
@@ -146,8 +149,10 @@
         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
         provider.setListener(listener);
 
-        verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
-        assertArrayEquals(new int[] { 1, 2 }, mIntArrayCaptor.getValue());
+        verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+        final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, null),
+                new DeviceState(2, null) };
+        assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
 
         verify(listener).onStateChanged(mIntegerCaptor.capture());
         assertEquals(1, mIntegerCaptor.getValue().intValue());
@@ -166,6 +171,7 @@
                 + "    </device-state>\n"
                 + "    <device-state>\n"
                 + "        <identifier>2</identifier>\n"
+                + "        <name>CLOSED</name>\n"
                 + "        <conditions>\n"
                 + "            <lid-switch>\n"
                 + "                <open>false</open>\n"
@@ -180,8 +186,10 @@
         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
         provider.setListener(listener);
 
-        verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
-        assertArrayEquals(new int[] { 1, 2 }, mIntArrayCaptor.getValue());
+        verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+        final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, null),
+                new DeviceState(2, "CLOSED") };
+        assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
 
         verify(listener).onStateChanged(mIntegerCaptor.capture());
         assertEquals(2, mIntegerCaptor.getValue().intValue());
@@ -189,8 +197,7 @@
         Mockito.clearInvocations(listener);
 
         provider.notifyLidSwitchChanged(0, true /* lidOpen */);
-
-        verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+        verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
         verify(listener).onStateChanged(mIntegerCaptor.capture());
         assertEquals(1, mIntegerCaptor.getValue().intValue());
     }
@@ -203,6 +210,7 @@
         String configString = "<device-state-config>\n"
                 + "    <device-state>\n"
                 + "        <identifier>1</identifier>\n"
+                + "        <name>CLOSED</name>\n"
                 + "        <conditions>\n"
                 + "            <sensor>\n"
                 + "                <type>" + sensor.getStringType() + "</type>\n"
@@ -215,6 +223,7 @@
                 + "    </device-state>\n"
                 + "    <device-state>\n"
                 + "        <identifier>2</identifier>\n"
+                + "        <name>HALF_OPENED</name>\n"
                 + "        <conditions>\n"
                 + "            <sensor>\n"
                 + "                <type>" + sensor.getStringType() + "</type>\n"
@@ -228,6 +237,7 @@
                 + "    </device-state>\n"
                 + "    <device-state>\n"
                 + "        <identifier>3</identifier>\n"
+                + "        <name>OPENED</name>\n"
                 + "        <conditions>\n"
                 + "            <sensor>\n"
                 + "                <type>" + sensor.getStringType() + "</type>\n"
@@ -246,8 +256,10 @@
         DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
         provider.setListener(listener);
 
-        verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
-        assertArrayEquals(new int[] { 1, 2, 3 }, mIntArrayCaptor.getValue());
+        verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+        assertArrayEquals(
+                new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"),
+                        new DeviceState(3, "OPENED") }, mDeviceStateArrayCaptor.getValue());
 
         verify(listener).onStateChanged(mIntegerCaptor.capture());
         assertEquals(1, mIntegerCaptor.getValue().intValue());
@@ -256,11 +268,11 @@
 
         SensorEvent event0 = mock(SensorEvent.class);
         event0.sensor = sensor;
-        FieldSetter.setField(event0, event0.getClass().getField("values"), new float[] { 180 });
+        FieldSetter.setField(event0, event0.getClass().getField("values"), new float[]{180});
 
         provider.onSensorChanged(event0);
 
-        verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+        verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
         verify(listener).onStateChanged(mIntegerCaptor.capture());
         assertEquals(3, mIntegerCaptor.getValue().intValue());
 
@@ -268,11 +280,11 @@
 
         SensorEvent event1 = mock(SensorEvent.class);
         event1.sensor = sensor;
-        FieldSetter.setField(event1, event1.getClass().getField("values"), new float[] { 90 });
+        FieldSetter.setField(event1, event1.getClass().getField("values"), new float[]{90});
 
         provider.onSensorChanged(event1);
 
-        verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+        verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
         verify(listener).onStateChanged(mIntegerCaptor.capture());
         assertEquals(2, mIntegerCaptor.getValue().intValue());
 
@@ -280,11 +292,11 @@
 
         SensorEvent event2 = mock(SensorEvent.class);
         event2.sensor = sensor;
-        FieldSetter.setField(event2, event2.getClass().getField("values"), new float[] { 0 });
+        FieldSetter.setField(event2, event2.getClass().getField("values"), new float[]{0});
 
         provider.onSensorChanged(event2);
 
-        verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+        verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
         verify(listener).onStateChanged(mIntegerCaptor.capture());
         assertEquals(1, mIntegerCaptor.getValue().intValue());
     }
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 84b690f..03e60af 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.hardware.power.stats.Channel;
 import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerAttribution;
 import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyMeasurement;
 import android.hardware.power.stats.PowerEntity;
@@ -73,6 +74,7 @@
     private static final String ENERGY_CONSUMER_NAME = "energyconsumer";
     private static final int ENERGY_METER_COUNT = 8;
     private static final int ENERGY_CONSUMER_COUNT = 2;
+    private static final int ENERGY_CONSUMER_ATTRIBUTION_COUNT = 5;
     private static final int POWER_ENTITY_COUNT = 3;
     private static final int STATE_INFO_COUNT = 5;
     private static final int STATE_RESIDENCY_COUNT = 4;
@@ -204,6 +206,13 @@
                 energyConsumedList[i].id = i;
                 energyConsumedList[i].timestampMs = i;
                 energyConsumedList[i].energyUWs = i;
+                energyConsumedList[i].attribution =
+                    new EnergyConsumerAttribution[ENERGY_CONSUMER_ATTRIBUTION_COUNT];
+                for (int j = 0; j < energyConsumedList[i].attribution.length; j++) {
+                    energyConsumedList[i].attribution[j] = new EnergyConsumerAttribution();
+                    energyConsumedList[i].attribution[j].uid = j;
+                    energyConsumedList[i].attribution[j].energyUWs = j;
+                }
             }
             return energyConsumedList;
         }
@@ -221,7 +230,7 @@
         }
 
         @Override
-        public EnergyMeasurement[] readEnergyMeters(int[] channelIds) {
+        public EnergyMeasurement[] readEnergyMeter(int[] channelIds) {
             EnergyMeasurement[] energyMeasurementList = new EnergyMeasurement[ENERGY_METER_COUNT];
             for (int i = 0; i < energyMeasurementList.length; i++) {
                 energyMeasurementList[i] = new EnergyMeasurement();
@@ -250,7 +259,7 @@
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         // Write data to on-device storage.
-        mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER);
+        mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
 
         // The above call puts a message on a handler.  Wait for
         // it to be processed.
@@ -293,7 +302,7 @@
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         // Write data to on-device storage.
-        mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER);
+        mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
 
         // The above call puts a message on a handler.  Wait for
         // it to be processed.
@@ -324,6 +333,12 @@
             assertTrue(pssProto.energyConsumerResult[i].id == i);
             assertTrue(pssProto.energyConsumerResult[i].timestampMs == i);
             assertTrue(pssProto.energyConsumerResult[i].energyUws == i);
+            assertTrue(pssProto.energyConsumerResult[i].attribution.length
+                    == ENERGY_CONSUMER_ATTRIBUTION_COUNT);
+            for (int j = 0; j < pssProto.energyConsumerResult[i].attribution.length; j++) {
+                assertTrue(pssProto.energyConsumerResult[i].attribution[j].uid == j);
+                assertTrue(pssProto.energyConsumerResult[i].attribution[j].energyUws  == j);
+            }
         }
     }
 
diff --git a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
index 4c82818..c6e35cf 100644
--- a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
+++ b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
@@ -26,21 +26,17 @@
 object MockitoUtils {
     val ANSWER_THROWS = Answer<Any?> {
         when (val name = it.method.name) {
-            "toString" -> return@Answer Answers.CALLS_REAL_METHODS.answer(it)
+            "toString" -> return@Answer try {
+                Answers.CALLS_REAL_METHODS.answer(it)
+            } catch (e: Exception) {
+                "failure calling toString"
+            }
             else -> {
                 val arguments = it.arguments
                         ?.takeUnless { it.isEmpty() }
-                        ?.mapIndexed { index, arg ->
-                            try {
-                                arg?.toString()
-                            } catch (e: Exception) {
-                                "toString[$index] threw ${e.message}"
-                            }
-                        }
-                        ?.joinToString()
-                        ?.let {
-                            "with $it"
-                        }
+                        ?.mapIndexed { index, arg -> arg.attemptToString(index) }
+                        ?.joinToString { it.attemptToString(null) }
+                        ?.let { "with $it" }
                         .orEmpty()
 
                 throw UnsupportedOperationException("${it.mock::class.java.simpleName}#$name " +
@@ -48,6 +44,19 @@
             }
         }
     }
+
+    // Sometimes mocks won't have a toString method, so try-catch and return some default
+    private fun Any?.attemptToString(id: Any? = null): String {
+        return try {
+            toString()
+        } catch (e: Exception) {
+            if (id == null) {
+                e.message ?: "ERROR"
+            } else {
+                "$id ${e.message}"
+            }
+        }
+    }
 }
 
 inline fun <reified T> mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.java).apply(block)
@@ -83,4 +92,4 @@
 inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit = {}) =
         spyThrowOnUnmocked<T>(null, block)
 
-inline fun <reified T : Any> nullable() = ArgumentMatchers.nullable(T::class.java)
\ No newline at end of file
+inline fun <reified T : Any> nullable() = ArgumentMatchers.nullable(T::class.java)
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index fc1cb70..acda4d5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -29,8 +29,8 @@
 import static android.app.NotificationManager.IMPORTANCE_MAX;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
 import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER;
 import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER;
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 57d5323..72c6028 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -33,8 +33,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.os.AtomsProto.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
 import static com.android.os.AtomsProto.DNDModeProto.ENABLED_FIELD_NUMBER;
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 de2cc76..9385110 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -679,8 +679,10 @@
                 new RemoteAnimationAdapter(new Stub() {
 
                     @Override
-                    public void onAnimationStart(RemoteAnimationTarget[] apps,
+                    public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                            RemoteAnimationTarget[] apps,
                             RemoteAnimationTarget[] wallpapers,
+                            RemoteAnimationTarget[] nonApps,
                             IRemoteAnimationFinishedCallback finishedCallback) {
                     }
 
@@ -1715,9 +1717,8 @@
             doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay(
                     any() /* window */,  any() /* attrs */,
                     anyInt() /* viewVisibility */, anyInt() /* displayId */,
-                    any() /* requestedVisibility */, any() /* outFrame */,
-                    any() /* outInputChannel */, any() /* outInsetsState */,
-                    any() /* outActiveControls */);
+                    any() /* requestedVisibility */, any() /* outInputChannel */,
+                    any() /* outInsetsState */, any() /* outActiveControls */);
             mAtm.mWindowManager.mStartingSurfaceController
                     .createTaskSnapshotSurface(activity, snapshot);
         } catch (RemoteException ignored) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 91b9449..71f1914 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -36,6 +36,7 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -70,8 +71,10 @@
 
     class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
         @Override
-        public void onAnimationStart(RemoteAnimationTarget[] apps,
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] apps,
                 RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
                 IRemoteAnimationFinishedCallback finishedCallback) {
             for (RemoteAnimationTarget target : apps) {
                 assertNotNull(target.startBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index f1e3609..83aca5e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -265,8 +265,10 @@
     private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
         boolean mCancelled = false;
         @Override
-        public void onAnimationStart(RemoteAnimationTarget[] apps,
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] apps,
                 RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
                 IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 0afdc58..b1ea4a5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -55,7 +55,6 @@
 import static org.mockito.Mockito.spy;
 import static org.testng.Assert.expectThrows;
 
-import android.app.WindowConfiguration;
 import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
@@ -673,77 +672,13 @@
     public void layoutHint_appWindow() {
         mWindow.mAttrs.setFitInsetsTypes(0);
 
-        final Rect outFrame = new Rect();
         final DisplayCutout.ParcelableWrapper outDisplayCutout =
                 new DisplayCutout.ParcelableWrapper();
         final InsetsState outState = new InsetsState();
 
-        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outFrame,
-                outState, true /* localClient */);
-
-        assertThat(outFrame, is(outState.getDisplayFrame()));
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-        assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
-                is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
-        assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
-                is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
-    }
-
-    @Test
-    public void layoutHint_appWindowInTask() {
-        mWindow.mAttrs.setFitInsetsTypes(0);
-
-        final Rect taskBounds = new Rect(100, 100, 200, 200);
-        final Task task = mWindow.getTask();
-        // Force the bounds because the task may resolve different bounds from Task#setBounds.
-        task.getWindowConfiguration().setBounds(taskBounds);
-
-        final Rect outFrame = new Rect();
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-        final InsetsState outState = new InsetsState();
-
-        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outState,
+        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outState,
                 true /* localClient */);
 
-        assertThat(outFrame, is(taskBounds));
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-        assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
-                is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
-        assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
-                is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
-    }
-
-    @Test
-    public void layoutHint_appWindowInTask_outsideContentFrame() {
-        mWindow.mAttrs.setFitInsetsTypes(0);
-
-        final InsetsState state =
-                mDisplayContent.getInsetsStateController().getRawInsetsState();
-        final Rect contentFrame = new Rect(state.getDisplayFrame());
-        contentFrame.inset(state.calculateInsets(contentFrame, Type.systemBars(),
-                false /* ignoreVisibility */));
-
-        // Task is in the nav bar area (usually does not happen, but this is similar enough to
-        // the possible overlap with the IME)
-        final Rect taskBounds = new Rect(100, contentFrame.bottom + 1,
-                200, contentFrame.bottom + 10);
-
-        final Task task = mWindow.getTask();
-        // Make the task floating.
-        task.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
-        // Force the bounds because the task may resolve different bounds from Task#setBounds.
-        task.getWindowConfiguration().setBounds(taskBounds);
-
-        final Rect outFrame = new Rect();
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-        final InsetsState outState = new InsetsState();
-
-        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outState,
-                true /* localClient */);
-
-        assertThat(outFrame, is(taskBounds));
         assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
         assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
                 is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index f75c98f..78074d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -26,7 +27,6 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 
@@ -70,13 +70,15 @@
         spyOn(wms);
         doAnswer(invocation -> {
             Object[] args = invocation.getArguments();
-            final IBinder token = (IBinder) args[0];
-            final int windowType = (int) args[1];
-            new WindowToken(mWm, token, windowType, true /* persistOnEmpty */,
-                    mDefaultDisplay, true /* ownerCanManageAppTokens */, 1000 /* ownerUid */,
-                    false /* roundedCornerOverlay */, true /* fromClientToken */);
-            return WindowManagerGlobal.ADD_OKAY;
-        }).when(wms).addWindowTokenWithOptions(any(), anyInt(), anyInt(), any(), anyString());
+            IBinder clientToken = (IBinder) args[0];
+            int displayId = (int) args[2];
+            DisplayContent dc = mWm.mRoot.getDisplayContent(displayId);
+            mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken,
+                    dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
+                    null /* options */);
+            return true;
+        }).when(wms).registerWindowContextListener(any(), eq(TYPE_INPUT_METHOD_DIALOG),
+                anyInt(), any());
 
         mSecondaryDisplay = new TestDisplayContent.Builder(mAtm, 1000, 1000).build();
 
@@ -95,14 +97,12 @@
 
         assertImeSwitchContextMetricsValidity(contextOnDefaultDisplay, mDefaultDisplay);
 
-        // Obtain the context again and check they are the same instance and match the display
-        // metrics of the secondary display.
+        // Obtain the context again and check if the window metrics match the IME container bounds
+        // of the secondary display.
         final Context contextOnSecondaryDisplay = mController.getSettingsContext(
                 mSecondaryDisplay.getDisplayId());
 
         assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mSecondaryDisplay);
-        assertThat(contextOnDefaultDisplay.getWindowContextToken())
-                .isEqualTo(contextOnSecondaryDisplay.getWindowContextToken());
     }
 
     private void assertImeSwitchContextMetricsValidity(Context context, DisplayContent dc) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index fa3e3ae..7714a6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -183,6 +183,10 @@
         mColor = Color.GREEN;
 
         assertTrue(mLetterbox.needsApplySurfaceChanges());
+
+        mLetterbox.applySurfaceChanges(mTransaction);
+
+        verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 1, 0});
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 409bad4..c6be987 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -493,7 +493,7 @@
         initializeRecentsAnimationController(mController, homeActivity);
 
         // Verify RecentsAnimationController will animate visible leaf task by default.
-        verify(mController).addAnimation(eq(leafTask), anyBoolean(), anyBoolean(), eq(null));
+        verify(mController).addAnimation(eq(leafTask), anyBoolean(), anyBoolean(), any());
         assertTrue(leafTask.isAnimatingByRecents());
 
         // Make sure isAnimatingByRecents will also return true when it called by the parent task.
@@ -543,6 +543,35 @@
         verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
     }
 
+    @Test
+    public void testCleanupAnimation_expectExitAnimationDone() {
+        mWm.setRecentsAnimationController(mController);
+        final ActivityRecord homeActivity = createHomeActivity();
+        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
+        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
+        activity.addWindow(win1);
+
+        initializeRecentsAnimationController(mController, homeActivity);
+        mController.startAnimation();
+
+        spyOn(win1);
+        spyOn(win1.mWinAnimator);
+        // Simulate when the window is exiting and cleanupAnimation invoked
+        // (e.g. screen off during RecentsAnimation animating), will expect the window receives
+        // onExitAnimationDone to destroy the surface when the removal is allowed.
+        win1.mWinAnimator.mSurfaceController = mock(WindowSurfaceController.class);
+        win1.mHasSurface = true;
+        win1.mAnimatingExit = true;
+        win1.mRemoveOnExit = true;
+        win1.mWindowRemovalAllowed = true;
+        mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+        verify(win1).onAnimationFinished(eq(ANIMATION_TYPE_RECENTS), any());
+        verify(win1).onExitAnimationDone();
+        verify(win1).destroySurface(eq(false), eq(false));
+        assertFalse(win1.mAnimatingExit);
+        assertFalse(win1.mHasSurface);
+    }
+
     private ActivityRecord createHomeActivity() {
         final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
                 .setParentTask(mRootHomeTask)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 2efd4b5..15e045c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -17,6 +17,9 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -100,15 +103,18 @@
                     new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, appsCaptor.getValue().length);
             final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -136,7 +142,7 @@
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
 
         adapter.onAnimationCancelled(mMockLeash);
         verify(mMockRunner).onAnimationCancelled();
@@ -149,7 +155,7 @@
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
 
         mClock.fastForward(2500);
         mHandler.timeAdvance();
@@ -170,7 +176,7 @@
                     null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
 
             mClock.fastForward(2500);
             mHandler.timeAdvance();
@@ -190,7 +196,7 @@
 
     @Test
     public void testZeroAnimations() {
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_NONE);
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
     }
 
@@ -199,7 +205,7 @@
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mController.createRemoteAnimationRecord(win.mActivityRecord,
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
     }
 
@@ -213,15 +219,18 @@
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
         mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
         final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+        final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                 ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-        verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+        verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                 finishedCaptor.capture());
         assertEquals(1, appsCaptor.getValue().length);
         assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
@@ -235,7 +244,7 @@
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         win.mActivityRecord.removeImmediately();
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
         verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
                 eq(adapter));
@@ -255,15 +264,18 @@
                             mFinishedCallback);
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, appsCaptor.getValue().length);
             final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -305,15 +317,18 @@
                             mFinishedCallback);
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, appsCaptor.getValue().length);
             final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -354,15 +369,18 @@
                     new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, wallpapersCaptor.getValue().length);
         } finally {
@@ -383,15 +401,18 @@
                     new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAPpsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAPpsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, wallpapersCaptor.getValue().length);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6f775cf..cc4d4ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -39,8 +39,6 @@
 import static com.android.server.wm.Task.ActivityState.STOPPED;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -119,13 +117,13 @@
     @Test
     public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
         removeGlobalMinSizeRestriction();
-        // Create landscape freeform display and a freeform app.
+        // create freeform display and a freeform app
         DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
                 .setCanRotate(false)
                 .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
         setUpApp(display);
 
-        // Put app window into portrait freeform and then make it a compat app.
+        // Put app window into freeform and then make it a compat app.
         final Rect bounds = new Rect(100, 100, 400, 600);
         mTask.setBounds(bounds);
         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -138,7 +136,7 @@
 
         final int density = mActivity.getConfiguration().densityDpi;
 
-        // Change display configuration to fullscreen.
+        // change display configuration to fullscreen
         Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
         c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
         display.onRequestedOverrideConfigurationChanged(c);
@@ -148,8 +146,6 @@
         assertEquals(bounds.width(), mActivity.getBounds().width());
         assertEquals(bounds.height(), mActivity.getBounds().height());
         assertEquals(density, mActivity.getConfiguration().densityDpi);
-        // Size compat mode is sandboxed at the activity level.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -175,12 +171,6 @@
         assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
         // The decor height should be a part of the effective bounds.
         assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
-        // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-        // Activity max bounds ignore notch, since an app can be shown past the notch (although app
-        // is currently limited by the notch).
-        assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
-                .isEqualTo(displayBounds.height());
 
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         assertFitted();
@@ -190,17 +180,9 @@
         assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
         // The notch is no longer on top.
         assertEquals(appBounds, mActivity.getBounds());
-        // Activity max bounds are sandboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
 
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertFitted();
-        // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-        // Activity max bounds ignore notch, since an app can be shown past the notch (although app
-        // is currently limited by the notch).
-        assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
-                .isEqualTo(displayBounds.height());
     }
 
     @Test
@@ -228,9 +210,6 @@
         assertEquals(originalBounds.width(), mActivity.getBounds().width());
         assertEquals(originalBounds.height(), mActivity.getBounds().height());
         assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
-        // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
-        // max aspect ratio.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
         assertScaled();
     }
 
@@ -238,13 +217,11 @@
     public void testFixedScreenBoundsWhenDisplaySizeChanged() {
         setUpDisplaySizeWithApp(1000, 2500);
         prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
-        final DisplayContent display = mActivity.mDisplayContent;
         assertFitted();
-        // Activity and task inherit bounds from TaskDisplayArea, since not sandboxed.
-        assertMaxBoundsInheritDisplayAreaBounds();
 
         final Rect origBounds = new Rect(mActivity.getBounds());
         final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+        final DisplayContent display = mActivity.mDisplayContent;
 
         // Change the size of current display.
         resizeDisplay(display, 1000, 2000);
@@ -261,8 +238,6 @@
         // The position of configuration bounds should be the same as compat bounds.
         assertEquals(mActivity.getBounds().left, currentBounds.left);
         assertEquals(mActivity.getBounds().top, currentBounds.top);
-        // Activity is sandboxed to the offset size compat bounds.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // Change display size to a different orientation
         resizeDisplay(display, 2000, 1000);
@@ -271,8 +246,6 @@
         assertEquals(origBounds.height(), currentBounds.height());
         assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
         assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
-        // Activity is sandboxed to the offset size compat bounds.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // The previous resize operation doesn't consider the rotation change after size changed.
         // These setups apply the requested orientation to rotation as real case that the top fixed
@@ -292,8 +265,6 @@
         assertEquals(origBounds.height(), currentBounds.height());
         assertEquals(offsetX, currentBounds.left);
         assertScaled();
-        // Activity is sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -309,8 +280,6 @@
         assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
         // The position should be horizontal centered.
         assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
-        // Activity max bounds should be sandboxed since it is letterboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
 
         mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
         // Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
@@ -322,8 +291,6 @@
         // It should keep non-attachable because the resolved bounds will be computed according to
         // the aspect ratio that won't match its parent bounds.
         assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
-        // Activity max bounds should be sandboxed since it is letterboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
     }
 
     @Test
@@ -349,13 +316,14 @@
     }
 
     @Test
-    public void testMoveToDifferentOrientationDisplay() {
+    public void testMoveToDifferentOrientDisplay() {
         setUpDisplaySizeWithApp(1000, 2500);
         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
         assertFitted();
 
-        final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
-        final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+        final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
+        final int origWidth = configBounds.width();
+        final int origHeight = configBounds.height();
 
         final int notchHeight = 100;
         final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
@@ -364,44 +332,37 @@
         // Move the non-resizable activity to the new display.
         mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
         // The configuration bounds [820, 0 - 1820, 2500] should keep the same.
-        assertEquals(originalBounds.width(), currentBounds.width());
-        assertEquals(originalBounds.height(), currentBounds.height());
+        assertEquals(origWidth, configBounds.width());
+        assertEquals(origHeight, configBounds.height());
         assertScaled();
-        // Activity max bounds are sandboxed due to size compat mode on the new display.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
         // The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
         assertEquals(newDisplayBounds.height() - notchHeight,
-                (int) ((float) mActivity.getBounds().width() * originalBounds.height()
-                        / originalBounds.width()));
+                (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
 
         // Recompute the natural configuration in the new display.
         mActivity.clearSizeCompatMode();
         mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
         // Because the display cannot rotate, the portrait activity will fit the short side of
         // display with keeping portrait bounds [200, 0 - 700, 1000] in center.
-        assertEquals(newDisplayBounds.height(), currentBounds.height());
-        assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
-                currentBounds.width());
+        assertEquals(newDisplayBounds.height(), configBounds.height());
+        assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+                configBounds.width());
         assertFitted();
         // The appBounds should be [200, 100 - 700, 1000].
         final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
-        assertEquals(currentBounds.width(), appBounds.width());
-        assertEquals(currentBounds.height() - notchHeight, appBounds.height());
-        // Task max bounds are sandboxed due to letterboxing from orientation mismatch with display.
-        assertTaskMaxBoundsSandboxed();
+        assertEquals(configBounds.width(), appBounds.width());
+        assertEquals(configBounds.height() - notchHeight, appBounds.height());
     }
 
     @Test
-    public void testFixedOrientationRotateCutoutDisplay() {
+    public void testFixedOrientRotateCutoutDisplay() {
         // Create a display with a notch/cutout
         final int notchHeight = 60;
-        final int width = 1000;
-        setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
+        setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
                 .setNotch(notchHeight).build());
-        // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
-        final float maxAspect = 1.4f;
+        // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
         prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
 
         final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
@@ -409,11 +370,6 @@
         final Rect origBounds = new Rect(currentBounds);
         final Rect origAppBounds = new Rect(appBounds);
 
-        // Activity is sandboxed, and bounds include the area consumed by the notch.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
-                .isEqualTo(Math.round(width * maxAspect) + notchHeight);
-
         // Although the activity is fixed orientation, force rotate the display.
         rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
         assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
@@ -429,13 +385,10 @@
         // The position in configuration should be global coordinates.
         assertEquals(mActivity.getBounds().left, currentBounds.left);
         assertEquals(mActivity.getBounds().top, currentBounds.top);
-
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
-    public void testFixedAspectRatioOrientationChangeOrientation() {
+    public void testFixedAspOrientChangeOrient() {
         setUpDisplaySizeWithApp(1000, 2500);
 
         final float maxAspect = 1.4f;
@@ -447,8 +400,6 @@
         final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
 
         assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
-        // Activity is sandboxed due to fixed aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
 
         // Change the fixed orientation.
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -460,8 +411,6 @@
                 mActivity.getWindowConfiguration().getAppBounds().height());
         assertEquals(originalAppBounds.height(),
                 mActivity.getWindowConfiguration().getAppBounds().width());
-        // Activity is sandboxed due to fixed aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
     }
 
     @Test
@@ -510,8 +459,6 @@
         // restarted and the override configuration won't be cleared.
         verify(mActivity, never()).restartProcessIfVisible();
         assertScaled();
-        // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // Change display density
         display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
@@ -586,16 +533,12 @@
         // in multi-window mode.
         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
         assertFalse(activity.shouldUseSizeCompatMode());
-        // Activity and task should not be sandboxed.
-        assertMaxBoundsInheritDisplayAreaBounds();
 
         // The non-resizable activity should not be size compat because the display support
         // changing windowing mode from fullscreen to freeform.
         mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
         assertFalse(activity.shouldUseSizeCompatMode());
-        // Activity and task should not be sandboxed.
-        assertMaxBoundsInheritDisplayAreaBounds();
     }
 
     @Test
@@ -659,9 +602,6 @@
         // be transparent.
         assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
 
-        // Activity is sandboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-
         // Make the activity fill the display.
         prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
         w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
@@ -671,7 +611,6 @@
         // The letterbox should only cover the notch area, so status bar can be transparent.
         assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
         assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
-        assertActivityMaxBoundsSandboxedForLetterbox();
     }
 
     @Test
@@ -696,8 +635,6 @@
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
         assertEquals(taskBounds, activityBounds);
-        // Activity inherits max bounds from task, since sandboxing applied to task.
-        assertTaskMaxBoundsSandboxed();
 
         // Task bounds should be 700x1400 with the ratio as the display.
         assertEquals(displayBounds.height(), taskBounds.height());
@@ -728,8 +665,6 @@
         assertScaled();
         assertEquals(activityBounds.width(), newActivityBounds.width());
         assertEquals(activityBounds.height(), newActivityBounds.height());
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -741,30 +676,29 @@
         // Portrait fixed app without max aspect.
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
+        Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+        Rect activityBounds = new Rect(mActivity.getBounds());
+
         // App should launch in fullscreen.
         assertFalse(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        // Activity and task inherit max bounds from TaskDisplayArea.
-        assertMaxBoundsInheritDisplayAreaBounds();
+        assertEquals(displayBounds, activityBounds);
 
         // Rotate display to landscape.
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
 
-        final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
-        final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
-        assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
+        displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+        activityBounds = new Rect(mActivity.getBounds());
+        assertTrue(displayBounds.width() > displayBounds.height());
 
         // App should be in size compat.
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
-        assertThat(mActivity.inSizeCompatMode()).isTrue();
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // App bounds should be 700x1400 with the ratio as the display.
-        assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
-        assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
-                        / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
+        assertEquals(displayBounds.height(), activityBounds.height());
+        assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+                activityBounds.width());
     }
 
     @Test
@@ -797,17 +731,14 @@
         final Rect displayBounds = new Rect(display.getBounds());
         final Rect taskBounds = new Rect(mTask.getBounds());
         final Rect newActivityBounds = new Rect(newActivity.getBounds());
-        final float displayAspectRatio = (float) displayBounds.height() / displayBounds.width();
 
         // Task and app bounds should be 700x1400 with the ratio as the display.
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(newActivity.inSizeCompatMode());
         assertEquals(taskBounds, newActivityBounds);
         assertEquals(displayBounds.height(), taskBounds.height());
-        assertThat(taskBounds.width())
-                .isEqualTo(Math.round(displayBounds.height() * displayAspectRatio));
-        // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
-        assertTaskMaxBoundsSandboxed();
+        assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+                taskBounds.width());
     }
 
     @Test
@@ -847,14 +778,6 @@
         assertEquals(displayBounds.height(), taskBounds.height());
         assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
                 taskBounds.width());
-        // New activity max bounds are sandboxed due to letterbox.
-        assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(taskBounds);
-        // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().height())
-                .isEqualTo(displayBounds.height());
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().width())
-                .isEqualTo(Math.round(displayBounds.height() / newActivity.info.maxAspectRatio));
 
         // App bounds should be fullscreen in Task bounds.
         assertFalse(newActivity.inSizeCompatMode());
@@ -883,9 +806,6 @@
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
         assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
-        assertThat(mActivity.inSizeCompatMode()).isTrue();
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         final Rect activityBounds = new Rect(mActivity.getBounds());
         mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
@@ -896,8 +816,6 @@
         assertScaled();
         assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
         assertEquals(activityBounds, mActivity.getBounds());
-        // Activity max bounds are sandboxed due to size compat.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -913,7 +831,6 @@
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        assertTaskMaxBoundsSandboxed();
 
         // Rotate display to portrait.
         rotateDisplay(display, ROTATION_90);
@@ -921,7 +838,6 @@
         // App should be in size compat.
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // Rotate display to landscape.
         rotateDisplay(display, ROTATION_180);
@@ -929,7 +845,6 @@
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        assertTaskMaxBoundsSandboxed();
     }
 
     @Test
@@ -947,26 +862,20 @@
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        // Task is letterboxed due to mismatched orientation request.
-        assertTaskMaxBoundsSandboxed();
 
-        // Rotate display to landscape.
+        // Rotate display to portrait.
         rotateDisplay(display, ROTATION_90);
 
         // App should be in size compat.
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
-        // Activity max bounds are sandboxed due to unresizable app.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
-        // Rotate display to portrait.
+        // Rotate display to landscape.
         rotateDisplay(display, ROTATION_180);
 
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        // Task is letterboxed, as in first case.
-        assertTaskMaxBoundsSandboxed();
     }
 
     @Test
@@ -983,18 +892,12 @@
         assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
         assertEquals(2800, displayBounds.width());
         assertEquals(1400, displayBounds.height());
-        Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
-        taskDisplayArea.setBounds(displayAreaBounds);
+        taskDisplayArea.setBounds(0, 0, 2400, 1000);
 
         final Rect activityBounds = new Rect(mActivity.getBounds());
         assertFalse(mActivity.inSizeCompatMode());
         assertEquals(2400, activityBounds.width());
         assertEquals(1000, activityBounds.height());
-        // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(displayAreaBounds);
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(displayAreaBounds);
     }
 
     @Test
@@ -1142,48 +1045,6 @@
         assertFalse(mActivity.hasSizeCompatBounds());
     }
 
-    /** Asserts both the activity and task max bounds inherit from the TaskDisplayArea. */
-    private void assertMaxBoundsInheritDisplayAreaBounds() {
-        final Rect taskDisplayAreaBounds = mTask.getDisplayArea().getBounds();
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(taskDisplayAreaBounds);
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(taskDisplayAreaBounds);
-    }
-
-    /**
-     * Asserts task-level letterboxing, so both activity and task max bounds
-     * are sandboxed to the letterbox bounds.
-     */
-    private void assertTaskMaxBoundsSandboxed() {
-        // Activity inherits max bounds from task, since sandboxing applied to task.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getBounds());
-        // Task max bounds are sandboxed due to letterbox.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getBounds());
-    }
-
-    /** Asserts activity-level size compat mode, so only activity max bounds are sandboxed. */
-    private void assertActivityMaxBoundsSandboxedForSizeCompat() {
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mActivity.getWindowConfiguration().getBounds());
-        // Task inherits max bounds from display.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getDisplayContent().getBounds());
-    }
-
-    /** Asserts activity-level letterboxing, so only activity max bounds are sandboxed. */
-    private void assertActivityMaxBoundsSandboxedForLetterbox() {
-        // Activity is sandboxed due to fixed aspect ratio.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mActivity.getBounds());
-        // Task inherits bounds from display.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getDisplayContent().getBounds());
-    }
-
     static Configuration rotateDisplay(DisplayContent display, int rotation) {
         final Configuration c = new Configuration();
         display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b8d44f6..6c72249 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -152,12 +152,6 @@
     }
 
     @Override
-    public SurfaceControl.Transaction reparentChildren(SurfaceControl sc,
-            SurfaceControl newParent) {
-        return this;
-    }
-
-    @Override
     public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) {
         return this;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index f20513d..0eb8c8d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -1004,6 +1004,26 @@
     }
 
     @Test
+    public void testNotSaveLaunchingStateForNonLeafTask() {
+        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+        spyOn(persister);
+
+        final Task task = getTestTask();
+        task.setHasBeenVisible(false);
+        task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+        task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        final Task leafTask = createTaskInStack(task, 0 /* userId */);
+
+        leafTask.setHasBeenVisible(true);
+        task.setHasBeenVisible(true);
+        task.onConfigurationChanged(task.getParent().getConfiguration());
+
+        verify(persister, never()).saveTask(same(task), any());
+        verify(persister).saveTask(same(leafTask), any());
+    }
+
+    @Test
     public void testNotSpecifyOrientationByFloatingTask() {
         final Task task = new TaskBuilder(mSupervisor)
                 .setCreateActivity(true).setCreateParentTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index d49956a..9372530 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -145,7 +145,7 @@
 
         assertThat(surface).isNotNull();
         verify(session).addToDisplay(any(), argThat(this::isTrustedOverlay), anyInt(), anyInt(),
-                any(), any(), any(), any(), any());
+                any(), any(), any(), any());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d71993d..ae85ceb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,11 +136,6 @@
             final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                     mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
             final TestDisplayContent newDisplay = createInternal(display);
-            // Ensure letterbox aspect ratio is not overridden on any device target.
-            // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by
-            // the below method, is set on some device form factors.
-            mService.mWindowManager.setTaskLetterboxAspectRatio(0);
-
             // disable the normal system decorations
             final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
             spyOn(displayPolicy);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index df5b48a..99c96bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -66,6 +66,7 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -905,8 +906,10 @@
         final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
                 new IRemoteAnimationRunner.Stub() {
                     @Override
-                    public void onAnimationStart(RemoteAnimationTarget[] apps,
+                    public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                            RemoteAnimationTarget[] apps,
                             RemoteAnimationTarget[] wallpapers,
+                            RemoteAnimationTarget[] nonApps,
                             IRemoteAnimationFinishedCallback finishedCallback) {
                         try {
                             finishedCallback.onAnimationFinished();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 9e8f8eaa..04dea3f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -163,8 +163,13 @@
         mValid = true;
         mSessionComponentName = new ComponentName(service.getPackageName(),
                 mInfo.getSessionService());
-        // TODO : Need to get the hotword detection service from the xml metadata
-        mHotwordDetectionComponentName = null;
+        final String hotwordDetectionServiceName = mInfo.getHotwordDetectionService();
+        if (hotwordDetectionServiceName != null) {
+            mHotwordDetectionComponentName = new ComponentName(service.getPackageName(),
+                    hotwordDetectionServiceName);
+        } else {
+            mHotwordDetectionComponentName = null;
+        }
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         IntentFilter filter = new IntentFilter();
@@ -388,7 +393,11 @@
         if (DEBUG) {
             Slog.d(TAG, "setHotwordDetectionConfigLocked");
         }
-
+        if (mHotwordDetectionComponentName == null) {
+            Slog.e(TAG, "Calling setHotwordDetectionConfigLocked, but hotword detection service"
+                    + " name not found");
+            return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+        }
         if (!isIsolatedProcessLocked(mHotwordDetectionComponentName)) {
             return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
         }
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 7cc233b..cac7b82 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -84,7 +84,6 @@
     static_libs: [
         "libviewcompiler",
     ],
-    test_suites: ["general-tests"],
 }
 
 cc_binary_host {
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
index 5f7d3f9..791e471 100644
--- a/startop/view_compiler/TEST_MAPPING
+++ b/startop/view_compiler/TEST_MAPPING
@@ -10,10 +10,6 @@
           "include-filter": "android.view.cts.PrecompiledLayoutTest"
         }
       ]
-    },
-    {
-      "name": "view-compiler-tests",
-      "host": true
     }
   ]
 }
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index b73ef9b..be5fae4 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.net.Uri;
@@ -67,7 +68,8 @@
          * Sets the participants for the resulting {@link ConnectionRequest}
          * @param participants The participants to which the {@link Connection} is to connect.
          */
-        public @NonNull Builder setParticipants(@Nullable List<Uri> participants) {
+        public @NonNull Builder setParticipants(
+                @SuppressLint("NullableCollection") @Nullable List<Uri> participants) {
             this.mParticipants = participants;
             return this;
         }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 5c75a2f..3b06fd3 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2459,7 +2459,7 @@
     }
 
     private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts) {
-        Log.i(this, "onCallFilteringCompleted(%s, %b, %b)", isBlocked, isInContacts);
+        Log.i(this, "onCallFilteringCompleted(%b, %b)", isBlocked, isInContacts);
         Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted");
         if (connection != null) {
             connection.onCallFilteringCompleted(isBlocked, isInContacts);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 6dc096d..88ef1b0 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -333,6 +333,8 @@
 
     void cleanupStuckCalls();
 
+    void resetCarMode();
+
     void setTestDefaultCallRedirectionApp(String packageName);
 
     void setTestPhoneAcctSuggestionComponent(String flattenedComponentName);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7215cd5..ac584c1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4038,6 +4038,20 @@
         public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL =
                 KEY_PREFIX + "enable_presence_group_subscribe_bool";
 
+        /**
+         * An integer key associated with the period of time in seconds the non-rcs capability
+         * information of each contact is cached on the device.
+         * <p>
+         * The rcs capability cache expiration sec is managed by
+         * {@code android.telephony.ims.ProvisioningManager} but non-rcs capability is managed by
+         * {@link CarrierConfigManager} since non-rcs capability will be provided via ACS or carrier
+         * config.
+         * <p>
+         * The default value is 2592000 secs (30 days), see RCC.07 Annex A.1.9.
+         */
+        public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT =
+                KEY_PREFIX + "non_rcs_capabilities_cache_expiration_sec_int";
+
         private Ims() {}
 
         private static PersistableBundle getDefaults() {
@@ -4048,6 +4062,7 @@
             defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
             defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
             defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
+            defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60);
             return defaults;
         }
     }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2734ad1..1473b7a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -47,6 +47,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelUuid;
@@ -150,6 +151,22 @@
     public static final String CACHE_KEY_SLOT_INDEX_PROPERTY =
             "cache_key.telephony.get_slot_index";
 
+    /** @hide */
+    public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings";
+
+    /** @hide */
+    public static final String RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME =
+            "restoreSimSpecificSettings";
+
+    /**
+     * Key to the backup & restore data byte array in the Bundle that is returned by {@link
+     * #getAllSimSpecificSettingsForBackup()} or to be pass in to {@link
+     * #restoreAllSimSpecificSettings()}.
+     *
+     * @hide
+     */
+    public static final String KEY_SIM_SPECIFIC_SETTINGS_DATA = "KEY_SIM_SPECIFIC_SETTINGS_DATA";
+
     private static final int MAX_CACHE_SIZE = 4;
 
     private static class VoidPropertyInvalidatedCache<T>
@@ -372,6 +389,28 @@
     public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
             CONTENT_URI, "wfc_roaming_enabled");
 
+
+    /**
+     * A content {@link uri} used to call the appropriate backup or restore method for sim-specific
+     * settings
+     * <p>
+     * See {@link #GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} and {@link
+     * #RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for information on what method to call.
+     * @hide
+     */
+    @NonNull
+    public static final Uri SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "backup_and_restore");
+
+    /**
+     * A content {@link uri} used to notify contentobservers listening to siminfo restore during
+     * SuW.
+     * @hide
+     */
+    @NonNull
+    public static final Uri SIM_INFO_SUW_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+            SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, "suw_restore");
+
     /**
      * A content {@link Uri} used to receive updates on cross sim enabled user setting.
      * <p>
@@ -3455,4 +3494,71 @@
         sSlotIndexCache.clear();
         sPhoneIdCache.clear();
     }
+
+    /**
+     * Called to retrieve SIM-specific settings data to be backed up.
+     *
+     * @return data in byte[] to be backed up.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public byte[] getAllSimSpecificSettingsForBackup() {
+        Bundle bundle =  mContext.getContentResolver().call(
+                SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null);
+        return bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA);
+    }
+
+    /**
+     * Called to attempt to restore the backed up sim-specific configs to device for specific sim.
+     * This will try to restore the data that was stored internally when {@link
+     * #restoreAllSimSpecificSettingsFromBackup(byte[] data)} was called during setup wizard.
+     * End result is SimInfoDB is modified to match any backed up configs for the requested
+     * inserted sim.
+     *
+     * <p>
+     * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+     * entry is updated as the result of this method call.
+     *
+     * @param iccId of the sim that a restore is requested for.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void restoreSimSpecificSettingsForIccIdFromBackup(@NonNull String iccId) {
+        mContext.getContentResolver().call(
+                SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+                iccId, null);
+    }
+
+    /**
+     * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
+     * configs to device for all existing SIMs in SimInfoDB. Internally, it will store the backup
+     * data in an internal file. This file will persist on device for device's lifetime and will be
+     * used later on when a SIM is inserted to restore that specific SIM's settings by calling
+     * {@link #restoreSimSpecificSettingsForIccIdFromBackup(String iccId)}. End result is
+     * SimInfoDB is modified to match any backed up configs for the appropriate inserted SIMs.
+     *
+     * <p>
+     * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+     * entry is updated as the result of this method call.
+     *
+     * @param data with the sim specific configs to be backed up.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) {
+        Bundle bundle = new Bundle();
+        bundle.putByteArray(KEY_SIM_SPECIFIC_SETTINGS_DATA, data);
+        mContext.getContentResolver().call(
+                SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+                null, bundle);
+    }
 }
diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
index fb65949..6bf992e 100644
--- a/telephony/java/android/telephony/ims/DelegateStateCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.telephony.ims.stub.SipDelegate;
 import android.telephony.ims.stub.SipTransportImplBase;
@@ -52,7 +53,9 @@
      *    implementing this feature elsewhere. If all features of this {@link SipDelegate} are
      *    denied, this method should still be called.
      */
-    void onCreated(@NonNull SipDelegate delegate, @Nullable Set<FeatureTagState> deniedTags);
+    void onCreated(@NonNull SipDelegate delegate,
+            @SuppressLint("NullableCollection")  // TODO(b/154763999): Mark deniedTags @Nonnull
+            @Nullable Set<FeatureTagState> deniedTags);
 
     /**
      * This must be called by the ImsService after the framework calls
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index f444c62..31ba853 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -25,6 +25,7 @@
 import android.annotation.StringDef;
 import android.annotation.SystemApi;
 import android.annotation.WorkerThread;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -1325,7 +1326,7 @@
      * the RCS VoLTE single registration feature. Only default messaging application may receive
      * the intent.
      *
-     * <p>Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to specify the subscription index for which
+     * <p>Contains {@link #EXTRA_SUBSCRIPTION_ID} to specify the subscription index for which
      * the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
      * status.
      */
@@ -1371,7 +1372,7 @@
      * provisioning is done using autoconfiguration, then these parameters shall be
      * sent in the HTTP get request to fetch the RCS provisioning. RCS client
      * configuration must be provided by the application before registering for the
-     * provisioning status events {@link #registerRcsProvisioningChangedCallback()}
+     * provisioning status events {@link #registerRcsProvisioningChangedCallback}
      * @param rcc RCS client configuration {@link RcsClientConfiguration}
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -1387,13 +1388,15 @@
     }
 
     /**
-     * Returns a flag to indicate if the device software and the carrier
-     * have the capability to support RCS Volte single IMS registration.
-     * @return true if this single registration is capable, false otherwise
+     * Returns a flag to indicate whether or not the device supports IMS single registration for
+     * MMTEL and RCS features as well as if the carrier has provisioned the feature.
+     * @return true if IMS single registration is capable at this time, or false otherwise
      * @throws ImsException If the remote ImsService is not available for
      * any reason or the subscription associated with this instance is no
      * longer active. See {@link ImsException#getCode()} for more
      * information.
+     * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this
+     * device supports IMS single registration.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
@@ -1430,7 +1433,7 @@
      * available. This can happen if the service crashed, for example.
      * It shall also throw this exception when the RCS client parameters for the
      * application are not valid. In that case application must set the client
-     * params (See {@link #setRcsClientConfiguration()}) and re register the
+     * params (See {@link #setRcsClientConfiguration}) and re register the
      * callback.
      * See {@link ImsException#getCode()} for a more detailed reason.
      */
@@ -1458,9 +1461,9 @@
      * will result in a no-op.
      * @param callback The existing {@link RcsProvisioningCallback} to be
      * removed.
-     * @see #registerRcsProvisioningChangedCallback(RcsClientConfiguration,
-     * Executor, RcsProvisioningCallback) @throws IllegalArgumentException
-     * if the subscription associated with this callback is invalid.
+     * @see #registerRcsProvisioningChangedCallback
+     * @throws IllegalArgumentException if the subscription associated with this callback is
+     * invalid.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterRcsProvisioningChangedCallback(
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 2e9eb94..04421c9 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -24,6 +24,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.telephony.BinderCacheManager;
@@ -47,6 +48,9 @@
  * This allows multiple IMS applications to forward SIP messages to/from their application for the
  * purposes of providing a single IMS registration to the carrier's IMS network from potentially
  * many IMS stacks implementing a subset of the supported MMTEL/RCS features.
+ * <p>
+ * This API is only supported if the device supports the
+ * {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION} feature.
  * @hide
  */
 @SystemApi
@@ -269,6 +273,7 @@
      * {@link ImsException#getCode()} for more information.
      *
      * @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL
+     * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isSupported() throws ImsException {
diff --git a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
index 481e7f8..b99d8a7d 100644
--- a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
@@ -26,4 +26,5 @@
 oneway interface IPublishResponseCallback {
     void onCommandError(int code);
     void onNetworkResponse(int code, String reason);
+    void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText);
 }
diff --git a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
index a141993..8cc8020 100644
--- a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
@@ -30,6 +30,7 @@
 oneway interface ISubscribeResponseCallback {
     void onCommandError(int code);
     void onNetworkResponse(int code, in String reason);
+    void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText);
     void onNotifyCapabilitiesUpdate(in List<String> pidfXmls);
     void onResourceTerminated(in List<RcsContactTerminatedReason> uriTerminatedReason);
     void onTerminated(in String reason, long retryAfterMilliseconds);
diff --git a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
index 22985d0..65415ea 100644
--- a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
@@ -34,10 +34,11 @@
     }
 
     @Override
-    public void onCommandError(int code) {
+    public void onCommandError(int code) throws ImsException {
         try {
             mResponseBinder.onCommandError(code);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
@@ -46,6 +47,18 @@
         try {
             mResponseBinder.onNetworkResponse(code, reason);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
+            String reasonHeaderText) throws ImsException {
+        try {
+            mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause,
+                    reasonHeaderText);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 }
diff --git a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
index 1fb339c..11118c0 100644
--- a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
@@ -40,10 +40,11 @@
     }
 
     @Override
-    public void onCommandError(int code) {
+    public void onCommandError(int code) throws ImsException {
         try {
             mResponseBinder.onCommandError(code);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
@@ -52,6 +53,18 @@
         try {
             mResponseBinder.onNetworkResponse(code, reason);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
+            String reasonHeaderText) throws ImsException {
+        try {
+            mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause,
+                    reasonHeaderText);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
@@ -60,6 +73,7 @@
         try {
             mResponseBinder.onNotifyCapabilitiesUpdate(pidfXmls);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
@@ -69,6 +83,7 @@
         try {
             mResponseBinder.onResourceTerminated(getTerminatedReasonList(uriTerminatedReason));
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
@@ -90,6 +105,7 @@
         try {
             mResponseBinder.onTerminated(reason, retryAfterMilliseconds);
         } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 }
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 2e35d27..5f8e93d 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -44,8 +44,12 @@
         @Override
         public void setListener(IImsEcbmListener listener) {
             synchronized (mLock) {
-                if (mImsEcbm != null && listener != null && Objects.equals(
-                        mImsEcbm.asBinder(), listener.asBinder())) {
+                if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mListener = null;
+                }
+                if (mListener != null && listener != null && Objects.equals(
+                        mListener.asBinder(), listener.asBinder())) {
                     return;
                 }
                 if (listener == null) {
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index 555a47e..8e961ac 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -48,6 +48,10 @@
         @Override
         public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
             synchronized (mLock) {
+                if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mListener = null;
+                }
                 if (mListener != null && listener != null && Objects.equals(
                         mListener.asBinder(), listener.asBinder())) {
                     return;
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index eef4fca..83b89aa 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -23,6 +23,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.telephony.ims.ImsUtListener;
+import android.util.Log;
 
 import com.android.ims.internal.IImsUt;
 import com.android.ims.internal.IImsUtListener;
@@ -41,6 +42,7 @@
 // will break other implementations of ImsUt maintained by other ImsServices.
 @SystemApi
 public class ImsUtImplBase {
+    private static final String TAG = "ImsUtImplBase";
     /**
      * Bar all incoming calls. (See 3GPP TS 24.611)
      * @hide
@@ -207,6 +209,11 @@
         @Override
         public void setListener(IImsUtListener listener) throws RemoteException {
             synchronized (mLock) {
+                if (mUtListener != null
+                        && !mUtListener.getListenerInterface().asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mUtListener = null;
+                }
                 if (mUtListener != null && listener != null && Objects.equals(
                         mUtListener.getListenerInterface().asBinder(), listener.asBinder())) {
                     return;
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 7eba709..ec98be6 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -140,6 +140,9 @@
          * Provide the framework with a subsequent network response update to
          * {@link #publishCapabilities(String, PublishResponseCallback)}.
          *
+         * If this network response also contains a “Reason” header, then the
+         * {@link onNetworkResponse(int, String, int, String)} method should be used instead.
+         *
          * @param sipCode The SIP response code sent from the network for the operation
          * token specified.
          * @param reason The optional reason response from the network. If there is a reason header
@@ -154,6 +157,31 @@
          */
         void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
                 @NonNull String reason) throws ImsException;
+
+        /**
+         * Provide the framework with a subsequent network response update to
+         * {@link #publishCapabilities(RcsContactUceCapability, int)} that also
+         * includes a reason provided in the “reason” header. See RFC3326 for more
+         * information.
+         *
+         * @param sipCode The SIP response code sent from the network.
+         * @param reasonPhrase The optional reason response from the network. If the
+         * network provided no reason with the sip code, the string should be empty.
+         * @param reasonHeaderCause The “cause” parameter of the “reason” header
+         * included in the SIP message.
+         * @param reasonHeaderText The “text” parameter of the “reason” header
+         * included in the SIP message.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework. This can happen if the
+         * {@link RcsFeature} is not
+         * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+         * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+         * rare cases when the Telephony stack has crashed.
+         */
+        void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+                @NonNull String reasonPhrase,
+                @IntRange(from = 100, to = 699) int reasonHeaderCause,
+                @NonNull String reasonHeaderText) throws ImsException;
     }
 
     /**
@@ -222,6 +250,9 @@
          * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the
          * subsequent NOTIFY responses to the subscription.
          *
+         * If this network response also contains a “Reason” header, then the
+         * {@link onNetworkResponse(int, String, int, String)} method should be used instead.
+         *
          * @param sipCode The SIP response code sent from the network for the operation
          * token specified.
          * @param reason The optional reason response from the network. If the network
@@ -236,6 +267,31 @@
                 @NonNull String reason) throws ImsException;
 
         /**
+         * Notify the framework  of the response to the SUBSCRIBE request from
+         * {@link #subscribeForCapabilities(RcsContactUceCapability, int)} that also
+         * includes a reason provided in the “reason” header. See RFC3326 for more
+         * information.
+         *
+         * @param sipCode The SIP response code sent from the network,
+         * @param reasonPhrase The optional reason response from the network. If the
+         * network provided no reason with the sip code, the string should be empty.
+         * @param reasonHeaderCause The “cause” parameter of the “reason” header
+         * included in the SIP message.
+         * @param reasonHeaderText The “text” parameter of the “reason” header
+         * included in the SIP message.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework. This can happen if the
+         * {@link RcsFeature} is not
+         * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+         * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+         * rare cases when the Telephony stack has crashed.
+         */
+        void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+                @NonNull String reasonPhrase,
+                @IntRange(from = 100, to = 699) int reasonHeaderCause,
+                @NonNull String reasonHeaderText) throws ImsException;
+
+        /**
          * Notify the framework of the latest XML PIDF documents included in the network response
          * for the requested contacts' capabilities requested by the Framework using
          * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}.
diff --git a/tests/UpdatableSystemFontTest/OWNERS b/tests/UpdatableSystemFontTest/OWNERS
new file mode 100644
index 0000000..34ac813
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 24939
+
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/tests/UpdatableSystemFontTest/TEST_MAPPING b/tests/UpdatableSystemFontTest/TEST_MAPPING
new file mode 100644
index 0000000..a5c4479
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "UpdatableSystemFontTest"
+    }
+  ]
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index eac8c292..0674138 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1262,22 +1262,28 @@
         }
     }
 
-    private void updateUidNetworkingBlocked() {
-        doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked(
-                i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */,
-                mRestrictBackground)
+    private void mockUidNetworkingBlocked() {
+        doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
+                .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
+                        i.getArgument(1) /* metered */, mRestrictBackground)
         ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
+
+        doAnswer(inv -> mContext.getSystemService(NetworkPolicyManager.class)
+                .checkUidNetworkingBlocked(inv.getArgument(0) /* uid */,
+                        inv.getArgument(1) /* uidRules */,
+                        inv.getArgument(2) /* isNetworkMetered */,
+                        inv.getArgument(3) /* isBackgroundRestricted */)
+        ).when(mNetworkPolicyManager).checkUidNetworkingBlocked(
+                anyInt(), anyInt(), anyBoolean(), anyBoolean());
     }
 
     private void setUidRulesChanged(int uidRules) throws RemoteException {
         mUidRules = uidRules;
-        updateUidNetworkingBlocked();
         mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules);
     }
 
     private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
         mRestrictBackground = restrictBackground;
-        updateUidNetworkingBlocked();
         mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground);
     }
 
@@ -6809,6 +6815,7 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .build();
         mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+        mockUidNetworkingBlocked();
 
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
@@ -6891,6 +6898,7 @@
     public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception {
         final TestNetworkCallback defaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultCallback);
+        mockUidNetworkingBlocked();
 
         // No Networkcallbacks invoked before any network is active.
         setUidRulesChanged(RULE_REJECT_ALL);
@@ -7160,6 +7168,13 @@
         when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
     }
 
+    private void establishLegacyLockdownVpn() throws Exception {
+        // The legacy lockdown VPN only supports userId 0.
+        final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+        mMockVpn.registerAgent(ranges);
+        mMockVpn.connect(true);
+    }
+
     @Test
     public void testLegacyLockdownVpn() throws Exception {
         mServiceContext.setPermission(
@@ -7254,22 +7269,30 @@
         mMockVpn.expectStartLegacyVpnRunner();
         b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
         ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
-        mMockVpn.establishForMyUid();
+        establishLegacyLockdownVpn();
         callback.expectAvailableThenValidatedCallbacks(mMockVpn);
         defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+        NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         b1.expectBroadcast();
         b2.expectBroadcast();
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+        assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
+        assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
 
         // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
         final LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName("wlan0");
         wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25"));
         wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0"));
-        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+        final NetworkCapabilities wifiNc = new NetworkCapabilities();
+        wifiNc.addTransportType(TRANSPORT_WIFI);
+        wifiNc.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc);
 
         b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
         // Wifi is CONNECTING because the VPN isn't up yet.
@@ -7302,16 +7325,20 @@
         // The VPN comes up again on wifi.
         b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
         b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
-        mMockVpn.establishForMyUid();
+        establishLegacyLockdownVpn();
         callback.expectAvailableThenValidatedCallbacks(mMockVpn);
         defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
         b1.expectBroadcast();
         b2.expectBroadcast();
-
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
+        assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+        assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI));
+        assertFalse(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+        assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
 
         // Disconnect cell. Nothing much happens since it's not the default network.
         // Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
@@ -8355,13 +8382,14 @@
     private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
             throws Exception {
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+        mMockVpn.setVpnType(vpnType);
         mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
-        mMockVpn.setVpnType(vpnType);
 
         final UnderlyingNetworkInfo underlyingNetworkInfo =
                 new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList<String>());
         mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo);
+        when(mDeps.getConnectionOwnerUid(anyInt(), any(), any())).thenReturn(42);
     }
 
     private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -8410,8 +8438,7 @@
         final int myUid = Process.myUid();
         setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_SERVICE);
 
-        // TODO: Test the returned UID
-        mService.getConnectionOwnerUid(getTestConnectionInfo());
+        assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
     }
 
     @Test
@@ -8421,8 +8448,7 @@
         mServiceContext.setPermission(
                 android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
 
-        // TODO: Test the returned UID
-        mService.getConnectionOwnerUid(getTestConnectionInfo());
+        assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
     }
 
     @Test
@@ -8433,8 +8459,7 @@
         mServiceContext.setPermission(
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED);
 
-        // TODO: Test the returned UID
-        mService.getConnectionOwnerUid(getTestConnectionInfo());
+        assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
     }
 
     private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index e590fb7..a10a3c8 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -55,7 +55,7 @@
     private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL)
 
     private val mMockService = mock(ConnectivityService::class.java).apply {
-        doReturn(false).`when`(this).isFallbackNetwork(any())
+        doReturn(false).`when`(this).isDefaultNetwork(any())
     }
     private val mTracker = LegacyTypeTracker(mMockService).apply {
         supportedTypes.forEach {
@@ -126,11 +126,11 @@
     fun testBroadcastOnDisconnect() {
         val mobileNai1 = mock(NetworkAgentInfo::class.java)
         val mobileNai2 = mock(NetworkAgentInfo::class.java)
-        doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai1)
+        doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1)
         mTracker.add(TYPE_MOBILE, mobileNai1)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE)
         reset(mMockService)
-        doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai2)
+        doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2)
         mTracker.add(TYPE_MOBILE, mobileNai2)
         verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt())
         mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index f478282..32c6a75 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -49,6 +49,7 @@
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -119,6 +120,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
@@ -213,6 +215,8 @@
 
         when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
         when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
+        when(mContext.getSystemServiceName(UserManager.class))
+                .thenReturn(Context.USER_SERVICE);
         when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
         when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
         when(mContext.getSystemServiceName(NotificationManager.class))
@@ -253,12 +257,14 @@
 
     @Test
     public void testRestrictedProfilesAreAddedToVpn() {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
 
         final Vpn vpn = createVpn(primaryUser.id);
-        final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
-                null, null);
+
+        // Assume the user can have restricted profiles.
+        doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+        final Set<UidRange> ranges =
+                vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
 
         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
                 PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
@@ -267,7 +273,6 @@
 
     @Test
     public void testManagedProfilesAreNotAddedToVpn() {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         setMockedUsers(primaryUser, managedProfileA);
 
         final Vpn vpn = createVpn(primaryUser.id);
@@ -290,7 +295,6 @@
 
     @Test
     public void testUidAllowAndDenylist() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
         final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
@@ -316,7 +320,6 @@
 
     @Test
     public void testGetAlwaysAndOnGetLockDown() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
 
         // Default state.
@@ -341,7 +344,6 @@
 
     @Test
     public void testLockdownChangingPackage() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
 
@@ -369,7 +371,6 @@
 
     @Test
     public void testLockdownAllowlist() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
 
@@ -444,7 +445,6 @@
 
     @Test
     public void testLockdownRuleRepeatability() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
                 new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
@@ -477,7 +477,6 @@
 
     @Test
     public void testLockdownRuleReversibility() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRangeParcel[] entireUser = {
             new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
@@ -954,7 +953,14 @@
     }
 
     private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
-        setMockedUsers(primaryUser);
+        // TODO(b/175883995): once these tests have been updated for the changes to the UserManager
+        // API, remove this ad-hoc setup code and use setMockedUsers(primaryUser) again.
+        // setMockedUsers(primaryUser);
+        final ArrayList<UserInfo> users = new ArrayList<>();
+        users.add(primaryUser);
+        when(mUserManager.getAliveUsers()).thenReturn(users);
+        when(mUserManager.getUserInfo(primaryUser.id)).thenReturn(primaryUser);
+        when(mUserManager.canHaveRestrictedProfile()).thenReturn(false);
 
         // Dummy egress interface
         final LinkProperties lp = new LinkProperties();
@@ -997,14 +1003,12 @@
         profile.ipsecIdentifier = "id";
         profile.ipsecSecret = "secret";
         profile.l2tpSecret = "l2tpsecret";
+
         when(mConnectivityManager.getAllNetworks())
             .thenReturn(new Network[] { new Network(101) });
+
         when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
-                anyInt(), any(), anyInt())).thenAnswer(invocation -> {
-                    // The runner has registered an agent and is now ready.
-                    legacyRunnerReady.open();
-                    return new Network(102);
-                });
+                anyInt(), any(), anyInt())).thenReturn(new Network(102));
         final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
         final TestDeps deps = (TestDeps) vpn.mDeps;
         try {
@@ -1020,14 +1024,20 @@
                             "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
                             "idle", "1800", "mtu", "1270", "mru", "1270" },
                     deps.mtpdArgs.get(10, TimeUnit.SECONDS));
+
             // Now wait for the runner to be ready before testing for the route.
-            legacyRunnerReady.block(10_000);
-            // In this test the expected address is always v4 so /32
+            ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class);
+            verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
+                    lpCaptor.capture(), any(), anyInt(), any(), anyInt());
+
+            // In this test the expected address is always v4 so /32.
+            // Note that the interface needs to be specified because RouteInfo objects stored in
+            // LinkProperties objects always acquire the LinkProperties' interface.
             final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"),
-                    RouteInfo.RTN_THROW);
-            assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : "
-                    + vpn.mConfig.routes,
-                    vpn.mConfig.routes.contains(expectedRoute));
+                    null, EGRESS_IFACE, RouteInfo.RTN_THROW);
+            final List<RouteInfo> actualRoutes = lpCaptor.getValue().getRoutes();
+            assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes,
+                    actualRoutes.contains(expectedRoute));
         } finally {
             // Now interrupt the thread, unblock the runner and clean up.
             vpn.mVpnRunner.exitVpnRunner();
@@ -1083,6 +1093,11 @@
         }
 
         @Override
+        public PendingIntent getIntentForStatusPanel(Context context) {
+            return null;
+        }
+
+        @Override
         public void sendArgumentsToDaemon(
                 final String daemon, final LocalSocket socket, final String[] arguments,
                 final Vpn.RetryScheduler interruptChecker) throws IOException {
@@ -1144,6 +1159,10 @@
         doReturn(UserHandle.of(userId)).when(asUserContext).getUser();
         when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
                 .thenReturn(asUserContext);
+        when(asUserContext.getSystemServiceName(UserManager.class))
+                .thenReturn(Context.USER_SERVICE);
+        when(asUserContext.getSystemService(UserManager.class))
+                .thenReturn(mUserManager);
         final TestLooper testLooper = new TestLooper();
         final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
                 mNetd, userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
@@ -1179,11 +1198,6 @@
             final int id = (int) invocation.getArguments()[0];
             return userMap.get(id);
         }).when(mUserManager).getUserInfo(anyInt());
-
-        doAnswer(invocation -> {
-            final int id = (int) invocation.getArguments()[0];
-            return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
-        }).when(mUserManager).canHaveRestrictedProfile();
     }
 
     /**
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index a7d0860..a07bce3 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -46,6 +46,8 @@
             "android.view.InsetsSourceTest",
             "android.view.InsetsSourceConsumerTest",
             "android.view.InsetsStateTest",
+            "android.view.RoundedCornerTest",
+            "android.view.RoundedCornersTest",
             "android.view.WindowMetricsTest",
             "android.view.PendingInsetsControllerTest",
             "android.app.WindowContextTest",
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 86a1591..3e659d0 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -59,12 +59,17 @@
 
     // Public for use in VcnGatewayConnectionTest
     public static VcnGatewayConnectionConfig buildTestConfig() {
+        return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
+    }
+
+    // Public for use in VcnGatewayConnectionTest
+    public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
         final VcnGatewayConnectionConfig.Builder builder =
                 new VcnGatewayConnectionConfig.Builder()
                         .setRetryInterval(RETRY_INTERVALS_MS)
                         .setMaxMtu(MAX_MTU);
 
-        for (int caps : EXPOSED_CAPS) {
+        for (int caps : exposedCaps) {
             builder.addExposedCapability(caps);
         }
 
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index e7d334e..4859644 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -66,6 +66,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
 import com.android.server.vcn.TelephonySubscriptionTracker;
 import com.android.server.vcn.Vcn;
 import com.android.server.vcn.VcnContext;
@@ -142,6 +143,9 @@
     private final TelephonySubscriptionTracker mSubscriptionTracker =
             mock(TelephonySubscriptionTracker.class);
 
+    private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor =
+            ArgumentCaptor.forClass(VcnSafemodeCallback.class);
+
     private final VcnManagementService mVcnMgmtSvc;
 
     private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
@@ -184,7 +188,7 @@
         doAnswer((invocation) -> {
             // Mock-within a doAnswer is safe, because it doesn't actually run nested.
             return mock(Vcn.class);
-        }).when(mMockDeps).newVcn(any(), any(), any());
+        }).when(mMockDeps).newVcn(any(), any(), any(), any(), any());
 
         final PersistableBundle bundle =
                 PersistableBundleUtils.fromMap(
@@ -304,14 +308,17 @@
 
     @Test
     public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception {
-        triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
-        verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG));
+        TelephonySubscriptionSnapshot snapshot =
+                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+        verify(mMockDeps)
+                .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
     }
 
     @Test
     public void testTelephonyNetworkTrackerCallbackStopsInstances() throws Exception {
         final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
         final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
 
         triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
 
@@ -319,6 +326,7 @@
         mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
         mTestLooper.dispatchAll();
         verify(vcn).teardownAsynchronously();
+        verify(mMockPolicyListener).onPolicyChanged();
     }
 
     @Test
@@ -389,6 +397,7 @@
             mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
             fail("Expected security exception for non system user");
         } catch (SecurityException expected) {
+            verify(mMockPolicyListener, never()).onPolicyChanged();
         }
     }
 
@@ -400,6 +409,7 @@
             mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
             fail("Expected security exception for missing carrier privileges");
         } catch (SecurityException expected) {
+            verify(mMockPolicyListener, never()).onPolicyChanged();
         }
     }
 
@@ -409,6 +419,7 @@
             mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, "IncorrectPackage");
             fail("Expected exception due to mismatched packages in config and method call");
         } catch (IllegalArgumentException expected) {
+            verify(mMockPolicyListener, never()).onPolicyChanged();
         }
     }
 
@@ -473,7 +484,13 @@
         verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
 
         // Verify Vcn is started
-        verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG));
+        verify(mMockDeps)
+                .newVcn(
+                        eq(mVcnContext),
+                        eq(TEST_UUID_2),
+                        eq(TEST_VCN_CONFIG),
+                        eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT),
+                        any());
 
         // Verify Vcn is updated if it was previously started
         mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
@@ -520,7 +537,7 @@
                 Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
     }
 
-    private void verifyMergedNetworkCapabilities(
+    private void verifyMergedNetworkCapabilitiesIsVcnManaged(
             NetworkCapabilities mergedCapabilities, @Transport int transportType) {
         assertTrue(mergedCapabilities.hasTransport(transportType));
         assertFalse(
@@ -529,7 +546,7 @@
     }
 
     @Test
-    public void testGetUnderlyingNetworkPolicyTransportCell() throws Exception {
+    public void testGetUnderlyingNetworkPolicyCellular() throws Exception {
         setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
 
         NetworkCapabilities nc =
@@ -542,12 +559,12 @@
                 mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
 
         assertFalse(policy.isTeardownRequested());
-        verifyMergedNetworkCapabilities(
+        verifyMergedNetworkCapabilitiesIsVcnManaged(
                 policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR);
     }
 
     @Test
-    public void testGetUnderlyingNetworkPolicyTransportWifi() throws Exception {
+    public void testGetUnderlyingNetworkPolicyWifi() throws Exception {
         setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
 
         WifiInfo wifiInfo = mock(WifiInfo.class);
@@ -563,7 +580,7 @@
                 mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
 
         assertFalse(policy.isTeardownRequested());
-        verifyMergedNetworkCapabilities(
+        verifyMergedNetworkCapabilitiesIsVcnManaged(
                 policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI);
     }
 
@@ -591,4 +608,56 @@
 
         mVcnMgmtSvc.getUnderlyingNetworkPolicy(new NetworkCapabilities(), new LinkProperties());
     }
+
+    @Test
+    public void testSubscriptionSnapshotUpdateNotifiesVcn() {
+        mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+        final Map<ParcelUuid, Vcn> vcnInstances = mVcnMgmtSvc.getAllVcns();
+        final Vcn vcnInstance = vcnInstances.get(TEST_UUID_2);
+
+        TelephonySubscriptionSnapshot snapshot =
+                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
+
+        verify(vcnInstance).updateSubscriptionSnapshot(eq(snapshot));
+    }
+
+    @Test
+    public void testAddNewVcnUpdatesPolicyListener() throws Exception {
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+
+        verify(mMockPolicyListener).onPolicyChanged();
+    }
+
+    @Test
+    public void testRemoveVcnUpdatesPolicyListener() throws Exception {
+        mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
+
+        verify(mMockPolicyListener).onPolicyChanged();
+    }
+
+    @Test
+    public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception {
+        TelephonySubscriptionSnapshot snapshot =
+                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+        verify(mMockDeps)
+                .newVcn(
+                        eq(mVcnContext),
+                        eq(TEST_UUID_1),
+                        eq(TEST_VCN_CONFIG),
+                        eq(snapshot),
+                        mSafemodeCallbackCaptor.capture());
+
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue();
+        safemodeCallback.onEnteredSafemode();
+
+        assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
+        verify(mMockPolicyListener).onPolicyChanged();
+    }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
index 48e068d..1d459a3 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -42,8 +42,9 @@
 import android.os.ParcelUuid;
 import android.os.test.TestLooper;
 import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
+import android.util.ArraySet;
 
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
 import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
@@ -58,13 +59,19 @@
 
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 
 public class UnderlyingNetworkTrackerTest {
     private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
     private static final int INITIAL_SUB_ID_1 = 1;
     private static final int INITIAL_SUB_ID_2 = 2;
+    private static final int UPDATED_SUB_ID = 3;
+
+    private static final Set<Integer> INITIAL_SUB_IDS =
+            new ArraySet<>(Arrays.asList(INITIAL_SUB_ID_1, INITIAL_SUB_ID_2));
+    private static final Set<Integer> UPDATED_SUB_IDS =
+            new ArraySet<>(Arrays.asList(UPDATED_SUB_ID));
 
     private static final NetworkCapabilities INITIAL_NETWORK_CAPABILITIES =
             new NetworkCapabilities.Builder()
@@ -90,7 +97,7 @@
     @Mock private Context mContext;
     @Mock private VcnNetworkProvider mVcnNetworkProvider;
     @Mock private ConnectivityManager mConnectivityManager;
-    @Mock private SubscriptionManager mSubscriptionManager;
+    @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
     @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
     @Mock private Network mNetwork;
 
@@ -113,23 +120,14 @@
                 mConnectivityManager,
                 Context.CONNECTIVITY_SERVICE,
                 ConnectivityManager.class);
-        setupSystemService(
-                mContext,
-                mSubscriptionManager,
-                Context.TELEPHONY_SUBSCRIPTION_SERVICE,
-                SubscriptionManager.class);
 
-        List<SubscriptionInfo> initialSubInfos =
-                Arrays.asList(
-                        getSubscriptionInfoForSubId(INITIAL_SUB_ID_1),
-                        getSubscriptionInfoForSubId(INITIAL_SUB_ID_2));
-        when(mSubscriptionManager.getSubscriptionsInGroup(eq(SUB_GROUP)))
-                .thenReturn(initialSubInfos);
+        when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
 
         mUnderlyingNetworkTracker =
                 new UnderlyingNetworkTracker(
                         mVcnContext,
                         SUB_GROUP,
+                        mSubscriptionSnapshot,
                         Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
                         mNetworkTrackerCb);
     }
@@ -154,23 +152,45 @@
                         eq(getWifiRequest()),
                         any(),
                         any(NetworkBringupCallback.class));
-        verify(mConnectivityManager)
-                .requestBackgroundNetwork(
-                        eq(getCellRequestForSubId(INITIAL_SUB_ID_1)),
-                        any(),
-                        any(NetworkBringupCallback.class));
-        verify(mConnectivityManager)
-                .requestBackgroundNetwork(
-                        eq(getCellRequestForSubId(INITIAL_SUB_ID_2)),
-                        any(),
-                        any(NetworkBringupCallback.class));
+        verifyBackgroundCellRequests(mSubscriptionSnapshot, SUB_GROUP, INITIAL_SUB_IDS);
+
         verify(mConnectivityManager)
                 .requestBackgroundNetwork(
                         eq(getRouteSelectionRequest()),
                         any(),
                         any(RouteSelectionCallback.class));
+    }
 
-        verify(mSubscriptionManager).getSubscriptionsInGroup(eq(SUB_GROUP));
+    private void verifyBackgroundCellRequests(
+            TelephonySubscriptionSnapshot snapshot,
+            ParcelUuid subGroup,
+            Set<Integer> expectedSubIds) {
+        verify(snapshot).getAllSubIdsInGroup(eq(subGroup));
+
+        for (final int subId : expectedSubIds) {
+            verify(mConnectivityManager)
+                    .requestBackgroundNetwork(
+                            eq(getCellRequestForSubId(subId)),
+                            any(),
+                            any(NetworkBringupCallback.class));
+        }
+    }
+
+    @Test
+    public void testUpdateSubscriptionSnapshot() {
+        // Verify initial cell background requests filed
+        verifyBackgroundCellRequests(mSubscriptionSnapshot, SUB_GROUP, INITIAL_SUB_IDS);
+
+        TelephonySubscriptionSnapshot subscriptionUpdate =
+                mock(TelephonySubscriptionSnapshot.class);
+        when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS);
+
+        mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate);
+
+        // verify that initially-filed bringup requests are unregistered
+        verify(mConnectivityManager, times(INITIAL_SUB_IDS.size()))
+                .unregisterNetworkCallback(any(NetworkBringupCallback.class));
+        verifyBackgroundCellRequests(subscriptionUpdate, SUB_GROUP, UPDATED_SUB_IDS);
     }
 
     private NetworkRequest getWifiRequest() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
new file mode 100644
index 0000000..e20070e
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 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.vcn;
+
+import static android.net.IpSecManager.DIRECTION_IN;
+import static android.net.IpSecManager.DIRECTION_OUT;
+
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.ConnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
+    private VcnIkeSession mIkeSession;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+
+        mIkeSession = mGatewayConnection.buildIkeSession();
+        mGatewayConnection.setIkeSession(mIkeSession);
+
+        mGatewayConnection.transitionTo(mGatewayConnection.mConnectedState);
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void testEnterStateCreatesNewIkeSession() throws Exception {
+        verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
+    }
+
+    @Test
+    public void testNullNetworkDoesNotTriggerDisconnect() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(null);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+        verify(mIkeSession, never()).close();
+    }
+
+    @Test
+    public void testNewNetworkTriggersMigration() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+        verify(mIkeSession, never()).close();
+        verify(mIkeSession).setNetwork(TEST_UNDERLYING_NETWORK_RECORD_2.network);
+    }
+
+    @Test
+    public void testSameNetworkDoesNotTriggerMigration() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testCreatedTransformsAreApplied() throws Exception {
+        for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
+            getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
+            mTestLooper.dispatchAll();
+
+            verify(mIpSecSvc)
+                    .applyTunnelModeTransform(
+                            eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
+        }
+
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testChildSessionClosedTriggersDisconnect() throws Exception {
+        getChildSessionCallback().onClosed();
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testIkeSessionClosedTriggersDisconnect() throws Exception {
+        getIkeSessionCallback().onClosed();
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+        verify(mIkeSession).close();
+    }
+
+    // TODO: Add tests for childOpened() when ChildSessionConfiguration can be mocked or created
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index 4ecd215..8643d8a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -44,7 +44,13 @@
     @Test
     public void testEnterWhileNotRunningTriggersQuit() throws Exception {
         final VcnGatewayConnection vgc =
-                new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+                new VcnGatewayConnection(
+                        mVcnContext,
+                        TEST_SUB_GRP,
+                        TEST_SUBSCRIPTION_SNAPSHOT,
+                        mConfig,
+                        mGatewayStatusCallback,
+                        mDeps);
 
         vgc.setIsRunning(false);
         vgc.transitionTo(vgc.mDisconnectedState);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index d741e5c..bc6bee2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -16,20 +16,36 @@
 
 package com.android.server.vcn;
 
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
-import android.annotation.NonNull;
-import android.content.Context;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.net.LinkProperties;
+import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
 import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
 import android.os.ParcelUuid;
-import android.os.test.TestLooper;
+import android.os.Process;
 import android.telephony.SubscriptionInfo;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -41,7 +57,9 @@
 /** Tests for TelephonySubscriptionTracker */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class VcnGatewayConnectionTest {
+public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
+    private static final int TEST_UID = Process.myUid();
+
     private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
     private static final int TEST_SIM_SLOT_INDEX = 1;
     private static final int TEST_SUBSCRIPTION_ID_1 = 2;
@@ -57,26 +75,67 @@
         TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap);
     }
 
-    @NonNull private final Context mContext;
-    @NonNull private final TestLooper mTestLooper;
-    @NonNull private final VcnNetworkProvider mVcnNetworkProvider;
-    @NonNull private final VcnGatewayConnection.Dependencies mDeps;
+    private WifiInfo mWifiInfo;
 
-    public VcnGatewayConnectionTest() {
-        mContext = mock(Context.class);
-        mTestLooper = new TestLooper();
-        mVcnNetworkProvider = mock(VcnNetworkProvider.class);
-        mDeps = mock(VcnGatewayConnection.Dependencies.class);
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mWifiInfo = mock(WifiInfo.class);
+    }
+
+    private void verifyBuildNetworkCapabilitiesCommon(int transportType) {
+        final NetworkCapabilities underlyingCaps = new NetworkCapabilities();
+        underlyingCaps.addTransportType(transportType);
+        underlyingCaps.addCapability(NET_CAPABILITY_NOT_METERED);
+        underlyingCaps.addCapability(NET_CAPABILITY_NOT_ROAMING);
+
+        if (transportType == TRANSPORT_WIFI) {
+            underlyingCaps.setTransportInfo(mWifiInfo);
+            underlyingCaps.setOwnerUid(TEST_UID);
+        } else if (transportType == TRANSPORT_CELLULAR) {
+            underlyingCaps.setAdministratorUids(new int[] {TEST_UID});
+            underlyingCaps.setNetworkSpecifier(
+                    new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_1));
+        }
+
+        UnderlyingNetworkRecord record =
+                new UnderlyingNetworkRecord(
+                        new Network(0), underlyingCaps, new LinkProperties(), false);
+        final NetworkCapabilities vcnCaps =
+                VcnGatewayConnection.buildNetworkCapabilities(
+                        VcnGatewayConnectionConfigTest.buildTestConfig(), record);
+
+        assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR));
+        assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+        assertArrayEquals(new int[] {TEST_UID}, vcnCaps.getAdministratorUids());
+        assertTrue(vcnCaps.getTransportInfo() instanceof VcnTransportInfo);
+
+        final VcnTransportInfo info = (VcnTransportInfo) vcnCaps.getTransportInfo();
+        if (transportType == TRANSPORT_WIFI) {
+            assertEquals(mWifiInfo, info.getWifiInfo());
+        } else if (transportType == TRANSPORT_CELLULAR) {
+            assertEquals(TEST_SUBSCRIPTION_ID_1, info.getSubId());
+        }
     }
 
     @Test
-    public void testBuildNetworkCapabilities() throws Exception {
-        final NetworkCapabilities caps =
-                VcnGatewayConnection.buildNetworkCapabilities(
-                        VcnGatewayConnectionConfigTest.buildTestConfig());
+    public void testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception {
+        verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI);
+    }
 
-        for (int exposedCapability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
-            assertTrue(caps.hasCapability(exposedCapability));
-        }
+    @Test
+    public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception {
+        verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR);
+    }
+
+    @Test
+    public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkTracker() {
+        final TelephonySubscriptionSnapshot updatedSnapshot =
+                mock(TelephonySubscriptionSnapshot.class);
+        mGatewayConnection.updateSubscriptionSnapshot(updatedSnapshot);
+
+        verify(mUnderlyingNetworkTracker).updateSubscriptionSnapshot(eq(updatedSnapshot));
     }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 4d92fb9..333b5b9 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -27,7 +27,9 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.net.IpSecConfig;
 import android.net.IpSecManager;
+import android.net.IpSecTransform;
 import android.net.IpSecTunnelInterfaceResponse;
 import android.net.LinkProperties;
 import android.net.Network;
@@ -40,15 +42,22 @@
 import android.os.test.TestLooper;
 
 import com.android.server.IpSecService;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
 
 import org.junit.Before;
 import org.mockito.ArgumentCaptor;
 
+import java.util.Collections;
 import java.util.UUID;
 
 public class VcnGatewayConnectionTestBase {
     protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
-    protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 1;
+    protected static final int TEST_IPSEC_SPI_VALUE = 0x1234;
+    protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
+    protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2;
+    protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 3;
+    protected static final int TEST_SUB_ID = 5;
     protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
     protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
             new UnderlyingNetworkRecord(
@@ -63,11 +72,16 @@
                     new LinkProperties(),
                     false /* blocked */);
 
+    protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT =
+            new TelephonySubscriptionSnapshot(
+                    Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP);
+
     @NonNull protected final Context mContext;
     @NonNull protected final TestLooper mTestLooper;
     @NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
     @NonNull protected final VcnContext mVcnContext;
     @NonNull protected final VcnGatewayConnectionConfig mConfig;
+    @NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
     @NonNull protected final VcnGatewayConnection.Dependencies mDeps;
     @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
 
@@ -82,6 +96,7 @@
         mVcnNetworkProvider = mock(VcnNetworkProvider.class);
         mVcnContext = mock(VcnContext.class);
         mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+        mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
         mDeps = mock(VcnGatewayConnection.Dependencies.class);
         mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
 
@@ -94,7 +109,7 @@
 
         doReturn(mUnderlyingNetworkTracker)
                 .when(mDeps)
-                .newUnderlyingNetworkTracker(any(), any(), any(), any());
+                .newUnderlyingNetworkTracker(any(), any(), any(), any(), any());
     }
 
     @Before
@@ -109,7 +124,18 @@
         mMockIkeSession = mock(VcnIkeSession.class);
         doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any());
 
-        mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+        mGatewayConnection =
+                new VcnGatewayConnection(
+                        mVcnContext,
+                        TEST_SUB_GRP,
+                        TEST_SUBSCRIPTION_SNAPSHOT,
+                        mConfig,
+                        mGatewayStatusCallback,
+                        mDeps);
+    }
+
+    protected IpSecTransform makeDummyIpSecTransform() throws Exception {
+        return new IpSecTransform(mContext, new IpSecConfig());
     }
 
     protected IkeSessionCallback getIkeSessionCallback() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
new file mode 100644
index 0000000..66cbf84
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2021 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.vcn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.NetworkRequest;
+import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Set;
+import java.util.UUID;
+
+public class VcnTest {
+    private static final String PKG_NAME = VcnTest.class.getPackage().getName();
+    private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+    private static final int NETWORK_SCORE = 0;
+    private static final int PROVIDER_ID = 5;
+
+    private Context mContext;
+    private VcnContext mVcnContext;
+    private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
+    private VcnNetworkProvider mVcnNetworkProvider;
+    private VcnSafemodeCallback mVcnSafemodeCallback;
+    private Vcn.Dependencies mDeps;
+
+    private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
+
+    private TestLooper mTestLooper;
+    private VcnGatewayConnectionConfig mGatewayConnectionConfig;
+    private VcnConfig mConfig;
+    private Vcn mVcn;
+
+    @Before
+    public void setUp() {
+        mContext = mock(Context.class);
+        mVcnContext = mock(VcnContext.class);
+        mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
+        mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+        mVcnSafemodeCallback = mock(VcnSafemodeCallback.class);
+        mDeps = mock(Vcn.Dependencies.class);
+
+        mTestLooper = new TestLooper();
+
+        doReturn(PKG_NAME).when(mContext).getOpPackageName();
+        doReturn(mContext).when(mVcnContext).getContext();
+        doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
+        doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
+
+        // Setup VcnGatewayConnection instance generation
+        doAnswer((invocation) -> {
+            // Mock-within a doAnswer is safe, because it doesn't actually run nested.
+            return mock(VcnGatewayConnection.class);
+        }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any());
+
+        mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
+
+        final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
+        for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+            configBuilder.addGatewayConnectionConfig(
+                    VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
+        }
+        configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
+        mConfig = configBuilder.build();
+
+        mVcn =
+                new Vcn(
+                        mVcnContext,
+                        TEST_SUB_GROUP,
+                        mConfig,
+                        mSubscriptionSnapshot,
+                        mVcnSafemodeCallback,
+                        mDeps);
+    }
+
+    private NetworkRequestListener verifyAndGetRequestListener() {
+        ArgumentCaptor<NetworkRequestListener> mNetworkRequestListenerCaptor =
+                ArgumentCaptor.forClass(NetworkRequestListener.class);
+        verify(mVcnNetworkProvider).registerListener(mNetworkRequestListenerCaptor.capture());
+
+        return mNetworkRequestListenerCaptor.getValue();
+    }
+
+    private void startVcnGatewayWithCapabilities(
+            NetworkRequestListener requestListener, int... netCapabilities) {
+        final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+        for (final int netCapability : netCapabilities) {
+            requestBuilder.addCapability(netCapability);
+        }
+
+        requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID);
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
+        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+        startVcnGatewayWithCapabilities(
+                requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
+
+        final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+        assertFalse(gatewayConnections.isEmpty());
+
+        final TelephonySubscriptionSnapshot updatedSnapshot =
+                mock(TelephonySubscriptionSnapshot.class);
+
+        mVcn.updateSubscriptionSnapshot(updatedSnapshot);
+        mTestLooper.dispatchAll();
+
+        for (final VcnGatewayConnection gateway : gatewayConnections) {
+            verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
+        }
+    }
+
+    @Test
+    public void testGatewayEnteringSafemodeNotifiesVcn() {
+        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+        for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+            startVcnGatewayWithCapabilities(requestListener, capability);
+        }
+
+        // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
+        // Expect one VcnGatewayConnection per capability.
+        final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
+
+        final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+        assertEquals(numExpectedGateways, gatewayConnections.size());
+        verify(mDeps, times(numExpectedGateways))
+                .newVcnGatewayConnection(
+                        eq(mVcnContext),
+                        eq(TEST_SUB_GROUP),
+                        eq(mSubscriptionSnapshot),
+                        any(),
+                        mGatewayStatusCallbackCaptor.capture());
+
+        // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
+        // all Gateways
+        final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+        statusCallback.onEnteredSafemode();
+        mTestLooper.dispatchAll();
+
+        for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
+            verify(gatewayConnection).teardownAsynchronously();
+        }
+        verify(mVcnNetworkProvider).unregisterListener(requestListener);
+        verify(mVcnSafemodeCallback).onEnteredSafemode();
+    }
+}
diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java
index 2140954..04f9bf2 100644
--- a/tools/powerstats/PowerStatsServiceProtoParser.java
+++ b/tools/powerstats/PowerStatsServiceProtoParser.java
@@ -86,6 +86,12 @@
                     csvRow += energyConsumerResult.getId() + ","
                         + energyConsumerResult.getTimestampMs() + ","
                         + energyConsumerResult.getEnergyUws() + ",";
+                    for (int k = 0; k < energyConsumerResult.getAttributionCount(); k++) {
+                        final EnergyConsumerAttributionProto energyConsumerAttribution =
+                                energyConsumerResult.getAttribution(k);
+                        csvRow += energyConsumerAttribution.getUid() + ","
+                            + energyConsumerAttribution.getEnergyUws() + ",";
+                    }
                 }
                 System.out.println(csvRow);
             }