Merge "Implement ComplicationTypesUpdater."
diff --git a/INPUT_OWNERS b/INPUT_OWNERS
new file mode 100644
index 0000000..6041f637f
--- /dev/null
+++ b/INPUT_OWNERS
@@ -0,0 +1,3 @@
+michaelwr@google.com
+prabirmsp@google.com
+svv@google.com
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
index 6d2bd47..2682dd7 100644
--- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
@@ -16,7 +16,7 @@
 
 package com.android.server.job;
 
-import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.util.proto.ProtoOutputStream;
@@ -47,9 +47,9 @@
     void removeBackingUpUid(int uid);
     void clearAllBackingUpUids();
 
-    /** Returns the package responsible for backing up media on the device. */
-    @NonNull
-    String getMediaBackupPackage();
+    /** Returns the package responsible for providing media from the cloud to the device. */
+    @Nullable
+    String getCloudMediaProviderPackage(int userId);
 
     /**
      * The user has started interacting with the app.  Take any appropriate action.
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 e23e067..bdfdd55 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -52,6 +52,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.BatteryManager;
@@ -70,6 +71,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.os.storage.StorageManagerInternal;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.format.DateUtils;
@@ -86,10 +88,10 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
@@ -237,6 +239,7 @@
     static final int MSG_UID_ACTIVE = 6;
     static final int MSG_UID_IDLE = 7;
     static final int MSG_CHECK_CHANGED_JOB_LIST = 8;
+    static final int MSG_CHECK_MEDIA_EXEMPTION = 9;
 
     /**
      * Track Services that have currently active or pending jobs. The index is provided by
@@ -271,8 +274,8 @@
     @GuardedBy("mLock")
     private final BatteryStateTracker mBatteryStateTracker;
 
-    @NonNull
-    private final String mSystemGalleryPackage;
+    @GuardedBy("mLock")
+    private final SparseArray<String> mCloudMediaProviderPackages = new SparseArray<>();
 
     private final CountQuotaTracker mQuotaTracker;
     private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()";
@@ -1783,9 +1786,6 @@
         mJobRestrictions = new ArrayList<>();
         mJobRestrictions.add(new ThermalStatusRestriction(this));
 
-        mSystemGalleryPackage = Objects.requireNonNull(
-                context.getString(R.string.config_systemGallery));
-
         // If the job store determined that it can't yet reschedule persisted jobs,
         // we need to start watching the clock.
         if (!mJobs.jobTimesInflatedValid()) {
@@ -1855,6 +1855,9 @@
             mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull(
                     LocalServices.getService(AppStateTracker.class));
 
+            LocalServices.getService(StorageManagerInternal.class)
+                    .registerCloudProviderChangeListener(new CloudProviderChangeListener());
+
             // Register br for package removals and user removals.
             final IntentFilter filter = new IntentFilter();
             filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
@@ -2353,6 +2356,15 @@
                         break;
                     }
 
+                    case MSG_CHECK_MEDIA_EXEMPTION: {
+                        final SomeArgs args = (SomeArgs) message.obj;
+                        synchronized (mLock) {
+                            updateMediaBackupExemptionLocked(
+                                    args.argi1, (String) args.arg1, (String) args.arg2);
+                        }
+                        args.recycle();
+                        break;
+                    }
                 }
                 maybeRunPendingJobsLocked();
             }
@@ -2649,12 +2661,31 @@
         if (DEBUG) {
             Slog.d(TAG, "Check changed jobs...");
         }
+        if (mChangedJobList.size() == 0) {
+            return;
+        }
 
         mChangedJobList.forEach(mMaybeQueueFunctor);
         mMaybeQueueFunctor.postProcessLocked();
         mChangedJobList.clear();
     }
 
+    @GuardedBy("mLock")
+    private void updateMediaBackupExemptionLocked(int userId, @Nullable String oldPkg,
+            @Nullable String newPkg) {
+        final Predicate<JobStatus> shouldProcessJob =
+                (job) -> job.getSourceUserId() == userId
+                        && (job.getSourcePackageName().equals(oldPkg)
+                        || job.getSourcePackageName().equals(newPkg));
+        mJobs.forEachJob(shouldProcessJob,
+                (job) -> {
+                    if (job.updateMediaBackupExemptionStatus()) {
+                        mChangedJobList.add(job);
+                    }
+                });
+        mHandler.sendEmptyMessage(MSG_CHECK_CHANGED_JOB_LIST);
+    }
+
     /** Returns true if both the calling and source users for the job are started. */
     @GuardedBy("mLock")
     public boolean areUsersStartedLocked(final JobStatus job) {
@@ -3050,8 +3081,8 @@
         }
 
         @Override
-        public String getMediaBackupPackage() {
-            return mSystemGalleryPackage;
+        public String getCloudMediaProviderPackage(int userId) {
+            return mCloudMediaProviderPackages.get(userId);
         }
 
         @Override
@@ -3159,6 +3190,35 @@
         return bucket;
     }
 
+    private class CloudProviderChangeListener implements
+            StorageManagerInternal.CloudProviderChangeListener {
+
+        @Override
+        public void onCloudProviderChanged(int userId, @Nullable String authority) {
+            final PackageManager pm = getContext()
+                    .createContextAsUser(UserHandle.of(userId), 0)
+                    .getPackageManager();
+            final ProviderInfo pi = pm.resolveContentProvider(
+                    authority, PackageManager.ComponentInfoFlags.of(0));
+            final String newPkg = (pi == null) ? null : pi.packageName;
+            synchronized (mLock) {
+                final String oldPkg = mCloudMediaProviderPackages.get(userId);
+                if (!Objects.equals(oldPkg, newPkg)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Cloud provider of user " + userId + " changed from " + oldPkg
+                                + " to " + newPkg);
+                    }
+                    mCloudMediaProviderPackages.put(userId, newPkg);
+                    SomeArgs args = SomeArgs.obtain();
+                    args.argi1 = userId;
+                    args.arg1 = oldPkg;
+                    args.arg2 = newPkg;
+                    mHandler.obtainMessage(MSG_CHECK_MEDIA_EXEMPTION, args).sendToTarget();
+                }
+            }
+        }
+    }
+
     /**
      * Binder stub trampoline implementation
      */
@@ -3799,6 +3859,12 @@
             pw.println();
 
             pw.println("Started users: " + Arrays.toString(mStartedUsers));
+            pw.println();
+
+            pw.print("Media Cloud Providers: ");
+            pw.println(mCloudMediaProviderPackages);
+            pw.println();
+
             pw.print("Registered ");
             pw.print(mJobs.size());
             pw.println(" jobs:");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index acbb1c1..649aa39 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -261,11 +261,9 @@
      *
      * Doesn't exempt jobs with a deadline constraint, as they can be started without any content or
      * network changes, in which case this exemption does not make sense.
-     *
-     * TODO(b/149519887): Use a more explicit signal, maybe an API flag, that the scheduling package
-     * needs to provide at the time of scheduling a job.
      */
-    private final boolean mHasMediaBackupExemption;
+    private boolean mHasMediaBackupExemption;
+    private final boolean mHasExemptedMediaUrisOnly;
 
     // Set to true if doze constraint was satisfied due to app being whitelisted.
     boolean appHasDozeExemption;
@@ -508,11 +506,9 @@
         this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
         this.numFailures = numFailures;
 
-        boolean requiresNetwork = false;
         int requiredConstraints = job.getConstraintFlags();
         if (job.getRequiredNetwork() != null) {
             requiredConstraints |= CONSTRAINT_CONNECTIVITY;
-            requiresNetwork = true;
         }
         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
             requiredConstraints |= CONSTRAINT_TIMING_DELAY;
@@ -531,6 +527,7 @@
                 }
             }
         }
+        mHasExemptedMediaUrisOnly = exemptedMediaUrisOnly;
         this.requiredConstraints = requiredConstraints;
         mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
         addDynamicConstraints(dynamicConstraints);
@@ -563,9 +560,7 @@
             job = builder.build(false);
         }
 
-        final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
-        mHasMediaBackupExemption = !job.hasLateConstraint() && exemptedMediaUrisOnly
-                && requiresNetwork && this.sourcePackageName.equals(jsi.getMediaBackupPackage());
+        updateMediaBackupExemptionStatus();
     }
 
     /** Copy constructor: used specifically when cloning JobStatus objects for persistence,
@@ -914,6 +909,25 @@
         mFirstForceBatchedTimeElapsed = now;
     }
 
+    /**
+     * Re-evaluates the media backup exemption status.
+     *
+     * @return true if the exemption status changed
+     */
+    public boolean updateMediaBackupExemptionStatus() {
+        final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
+        boolean hasMediaExemption = mHasExemptedMediaUrisOnly
+                && !job.hasLateConstraint()
+                && job.getRequiredNetwork() != null
+                && getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT
+                && sourcePackageName.equals(jsi.getCloudMediaProviderPackage(sourceUserId));
+        if (mHasMediaBackupExemption == hasMediaExemption) {
+            return false;
+        }
+        mHasMediaBackupExemption = hasMediaExemption;
+        return true;
+    }
+
     public String getSourceTag() {
         return sourceTag;
     }
@@ -2027,6 +2041,7 @@
                     TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw);
                     pw.println();
                 }
+                pw.print("Has media backup exemption", mHasMediaBackupExemption).println();
             }
             if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
                 pw.print("Extras: ");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 19f2537..b96055f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -1099,7 +1099,7 @@
         final long maxExecutionTimeRemainingMs =
                 mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs;
 
-        if (maxExecutionTimeRemainingMs <= 0) {
+        if (maxExecutionTimeRemainingMs < 0) {
             return 0;
         }
 
@@ -1110,7 +1110,7 @@
                     sessions, startMaxElapsed, maxExecutionTimeRemainingMs);
         }
 
-        if (allowedTimeRemainingMs <= 0) {
+        if (allowedTimeRemainingMs < 0) {
             return 0;
         }
 
diff --git a/core/api/current.txt b/core/api/current.txt
index 77f869a..ea3830f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -866,6 +866,7 @@
     field public static final int installLocation = 16843447; // 0x10102b7
     field public static final int interactiveUiTimeout = 16844181; // 0x1010595
     field public static final int interpolator = 16843073; // 0x1010141
+    field public static final int intro;
     field public static final int isAccessibilityTool = 16844353; // 0x1010641
     field public static final int isAlwaysSyncable = 16843571; // 0x1010333
     field public static final int isAsciiCapable = 16843753; // 0x10103e9
@@ -3247,6 +3248,7 @@
     method @Nullable public String getTileServiceClassName();
     method public boolean isAccessibilityTool();
     method public String loadDescription(android.content.pm.PackageManager);
+    method @Nullable public CharSequence loadIntro(@NonNull android.content.pm.PackageManager);
     method public CharSequence loadSummary(android.content.pm.PackageManager);
     method public void setInteractiveUiTimeoutMillis(@IntRange(from=0) int);
     method public void setNonInteractiveUiTimeoutMillis(@IntRange(from=0) int);
@@ -6634,6 +6636,7 @@
     method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>);
     method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational);
     method @NonNull public android.app.PictureInPictureParams.Builder setAutoEnterEnabled(boolean);
+    method @NonNull public android.app.PictureInPictureParams.Builder setExpandedAspectRatio(@Nullable android.util.Rational);
     method @NonNull public android.app.PictureInPictureParams.Builder setSeamlessResizeEnabled(boolean);
     method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect);
   }
@@ -7046,6 +7049,7 @@
   public final class VoiceInteractor {
     method public android.app.VoiceInteractor.Request getActiveRequest(String);
     method public android.app.VoiceInteractor.Request[] getActiveRequests();
+    method @NonNull public String getPackageName();
     method public boolean isDestroyed();
     method public void notifyDirectActionsChanged();
     method public boolean registerOnDestroyedCallback(@NonNull java.util.concurrent.Executor, @NonNull Runnable);
@@ -7887,6 +7891,8 @@
     field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
     field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
     field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
+    field public static final int TAG_BLUETOOTH_CONNECTION = 210039; // 0x33477
+    field public static final int TAG_BLUETOOTH_DISCONNECTION = 210040; // 0x33478
     field public static final int TAG_CAMERA_POLICY_SET = 210034; // 0x33472
     field public static final int TAG_CERT_AUTHORITY_INSTALLED = 210029; // 0x3346d
     field public static final int TAG_CERT_AUTHORITY_REMOVED = 210030; // 0x3346e
@@ -7909,6 +7915,7 @@
     field public static final int TAG_MEDIA_UNMOUNT = 210014; // 0x3345e
     field public static final int TAG_OS_SHUTDOWN = 210010; // 0x3345a
     field public static final int TAG_OS_STARTUP = 210009; // 0x33459
+    field public static final int TAG_PASSWORD_CHANGED = 210036; // 0x33474
     field public static final int TAG_PASSWORD_COMPLEXITY_REQUIRED = 210035; // 0x33473
     field public static final int TAG_PASSWORD_COMPLEXITY_SET = 210017; // 0x33461
     field public static final int TAG_PASSWORD_EXPIRATION_SET = 210016; // 0x33460
@@ -7918,6 +7925,8 @@
     field public static final int TAG_SYNC_SEND_FILE = 210004; // 0x33454
     field public static final int TAG_USER_RESTRICTION_ADDED = 210027; // 0x3346b
     field public static final int TAG_USER_RESTRICTION_REMOVED = 210028; // 0x3346c
+    field public static final int TAG_WIFI_CONNECTION = 210037; // 0x33475
+    field public static final int TAG_WIFI_DISCONNECTION = 210038; // 0x33476
     field public static final int TAG_WIPE_FAILURE = 210023; // 0x33467
   }
 
@@ -11859,6 +11868,7 @@
     field public static final String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
     field public static final String FEATURE_EMBEDDED = "android.hardware.type.embedded";
     field public static final String FEATURE_ETHERNET = "android.hardware.ethernet";
+    field public static final String FEATURE_EXPANDED_PICTURE_IN_PICTURE = "android.software.expanded_picture_in_picture";
     field public static final String FEATURE_FACE = "android.hardware.biometrics.face";
     field public static final String FEATURE_FAKETOUCH = "android.hardware.faketouch";
     field public static final String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
@@ -17370,6 +17380,7 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_PREVIEW_STABILIZATION_OUTPUT_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS;
@@ -18350,8 +18361,8 @@
   }
 
   public final class DisplayManager {
-    method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, int, int, int, @Nullable android.view.Surface, int);
-    method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, int, int, int, @Nullable android.view.Surface, int, @Nullable android.hardware.display.VirtualDisplay.Callback, @Nullable android.os.Handler);
+    method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int);
+    method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable android.hardware.display.VirtualDisplay.Callback, @Nullable android.os.Handler);
     method public android.view.Display getDisplay(int);
     method public android.view.Display[] getDisplays();
     method public android.view.Display[] getDisplays(String);
@@ -24368,6 +24379,17 @@
 
 package android.media.metrics {
 
+  public final class BundleSession implements java.lang.AutoCloseable {
+    method public void close();
+    method @NonNull public android.media.metrics.LogSessionId getSessionId();
+    method public void reportBundleMetrics(@NonNull android.os.PersistableBundle);
+  }
+
+  public final class EditingSession implements java.lang.AutoCloseable {
+    method public void close();
+    method @NonNull public android.media.metrics.LogSessionId getSessionId();
+  }
+
   public abstract class Event {
     method @NonNull public android.os.Bundle getMetricsBundle();
     method @IntRange(from=0xffffffff) public long getTimeSinceCreatedMillis();
@@ -24379,8 +24401,11 @@
   }
 
   public final class MediaMetricsManager {
+    method @NonNull public android.media.metrics.BundleSession createBundleSession();
+    method @NonNull public android.media.metrics.EditingSession createEditingSession();
     method @NonNull public android.media.metrics.PlaybackSession createPlaybackSession();
     method @NonNull public android.media.metrics.RecordingSession createRecordingSession();
+    method @NonNull public android.media.metrics.TranscodingSession createTranscodingSession();
     field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
   }
 
@@ -24628,6 +24653,11 @@
     method @NonNull public android.media.metrics.TrackChangeEvent.Builder setWidth(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
   }
 
+  public final class TranscodingSession implements java.lang.AutoCloseable {
+    method public void close();
+    method @NonNull public android.media.metrics.LogSessionId getSessionId();
+  }
+
 }
 
 package android.media.midi {
@@ -39500,9 +39530,9 @@
 
   public static final class RecognitionSupport.Builder {
     ctor public RecognitionSupport.Builder();
-    method @NonNull public android.speech.RecognitionSupport.Builder addInstalledLanguages(@NonNull String);
-    method @NonNull public android.speech.RecognitionSupport.Builder addPendingLanguages(@NonNull String);
-    method @NonNull public android.speech.RecognitionSupport.Builder addSupportedLanguages(@NonNull String);
+    method @NonNull public android.speech.RecognitionSupport.Builder addInstalledLanguage(@NonNull String);
+    method @NonNull public android.speech.RecognitionSupport.Builder addPendingLanguage(@NonNull String);
+    method @NonNull public android.speech.RecognitionSupport.Builder addSupportedLanguage(@NonNull String);
     method @NonNull public android.speech.RecognitionSupport build();
     method @NonNull public android.speech.RecognitionSupport.Builder setInstalledLanguages(@NonNull java.util.List<java.lang.String>);
     method @NonNull public android.speech.RecognitionSupport.Builder setPendingLanguages(@NonNull java.util.List<java.lang.String>);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 24b4f89..594f46b 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -415,6 +415,7 @@
     method public long computeStorageCacheBytes(@NonNull java.io.File);
     method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int);
     method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int);
+    method public void setCloudMediaProvider(@Nullable String);
     field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1; // 0x1
     field public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0; // 0x0
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5e5f387..584abc9 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -355,6 +355,7 @@
     field public static final String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS";
     field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
     field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
+    field public static final String WRITE_SECURITY_LOG = "android.permission.WRITE_SECURITY_LOG";
     field public static final String WRITE_SMS = "android.permission.WRITE_SMS";
   }
 
@@ -1307,6 +1308,10 @@
     field public static final int ERROR_UNKNOWN = 0; // 0x0
   }
 
+  public class SecurityLog {
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURITY_LOG) public static int writeEvent(int, @NonNull java.lang.Object...);
+  }
+
   public final class SystemUpdatePolicy implements android.os.Parcelable {
     method public android.app.admin.SystemUpdatePolicy.InstallationOption getInstallationOptionAt(long);
     field public static final int TYPE_PAUSE = 4; // 0x4
@@ -1647,6 +1652,7 @@
     method public int getResultNumber();
     method public int getResultOffset();
     method @NonNull public android.os.Bundle getSearchConstraints();
+    method @NonNull public String getSource();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final String CONSTRAINT_IS_PRESUBMIT_SUGGESTION = "IS_PRESUBMIT_SUGGESTION";
     field public static final String CONSTRAINT_SEARCH_PROVIDER_FILTER = "SEARCH_PROVIDER_FILTER";
@@ -2755,7 +2761,7 @@
     method public void addActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener, @NonNull java.util.concurrent.Executor);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
-    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(int, int, int, @Nullable android.view.Surface, int, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback);
+    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @NonNull java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
@@ -6279,6 +6285,9 @@
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getHeadTrackingMode();
     method @IntRange(from=0) @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getOutput();
     method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<java.lang.Integer> getSupportedHeadTrackingModes();
+    method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public boolean hasHeadTracker(@NonNull android.media.AudioDeviceAttributes);
+    method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public boolean isAvailableForDevice(@NonNull android.media.AudioDeviceAttributes);
+    method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public boolean isHeadTrackerEnabled(@NonNull android.media.AudioDeviceAttributes);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void recenterHeadTracker();
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeOnHeadTrackingModeChangedListener(@NonNull android.media.Spatializer.OnHeadTrackingModeChangedListener);
@@ -6286,6 +6295,7 @@
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setEffectParameter(int, @NonNull byte[]);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setEnabled(boolean);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setGlobalTransform(@NonNull float[]);
+    method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setHeadTrackerEnabled(boolean, @NonNull android.media.AudioDeviceAttributes);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setOnHeadToSoundstagePoseUpdatedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadToSoundstagePoseUpdatedListener);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setOnSpatializerOutputChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerOutputChangedListener);
     field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_DISABLED = -1; // 0xffffffff
@@ -9700,7 +9710,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean hasRestrictedProfiles();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isAdminUser();
-    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile();
+    method public boolean isCloneProfile();
     method public boolean isCredentialSharedWithParent();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isGuestUser();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bcba21b0..84b393a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -352,6 +352,7 @@
   public final class PictureInPictureParams implements android.os.Parcelable {
     method public java.util.List<android.app.RemoteAction> getActions();
     method public float getAspectRatio();
+    method public float getExpandedAspectRatio();
     method public android.graphics.Rect getSourceRectHint();
     method public boolean isSeamlessResizeEnabled();
   }
@@ -605,6 +606,14 @@
 
 }
 
+package android.app.cloudsearch {
+
+  public static final class SearchRequest.Builder {
+    method @NonNull public android.app.cloudsearch.SearchRequest.Builder setSource(@NonNull String);
+  }
+
+}
+
 package android.app.contentsuggestions {
 
   public final class ContentSuggestionsManager {
@@ -1959,6 +1968,7 @@
     method public long computeStorageCacheBytes(@NonNull java.io.File);
     method @NonNull public static java.util.UUID convert(@NonNull String);
     method @NonNull public static String convert(@NonNull java.util.UUID);
+    method @Nullable public String getCloudMediaProvider();
     method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int);
     method public static boolean isUserKeyUnlocked(int);
     field public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high";
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 85e7854..8af68d7 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -569,6 +569,11 @@
     private String mNonLocalizedSummary;
 
     /**
+     * Resource id of the intro of the accessibility service.
+     */
+    private int mIntroResId;
+
+    /**
      * Resource id of the description of the accessibility service.
      */
     private int mDescriptionResId;
@@ -737,6 +742,11 @@
                     R.styleable.AccessibilityService_isAccessibilityTool, false);
             mTileServiceClassName = asAttributes.getString(
                     com.android.internal.R.styleable.AccessibilityService_tileService);
+            peekedValue = asAttributes.peekValue(
+                    com.android.internal.R.styleable.AccessibilityService_intro);
+            if (peekedValue != null) {
+                mIntroResId = peekedValue.resourceId;
+            }
             asAttributes.recycle();
         } catch (NameNotFoundException e) {
             throw new XmlPullParserException( "Unable to create context for: "
@@ -957,6 +967,29 @@
     }
 
     /**
+     * The localized intro of the accessibility service.
+     * <p>
+     *    <strong>Statically set from
+     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
+     * </p>
+     * @return The localized intro if available, and {@code null} if a intro
+     * has not been provided.
+     */
+    @Nullable
+    public CharSequence loadIntro(@NonNull PackageManager packageManager) {
+        if (mIntroResId == /* invalid */ 0) {
+            return null;
+        }
+        ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
+        CharSequence intro = packageManager.getText(serviceInfo.packageName,
+                mIntroResId, serviceInfo.applicationInfo);
+        if (intro != null) {
+            return intro.toString().trim();
+        }
+        return null;
+    }
+
+    /**
      * Gets the non-localized description of the accessibility service.
      * <p>
      *    <strong>Statically set from
@@ -1114,6 +1147,7 @@
         parcel.writeString(mNonLocalizedDescription);
         parcel.writeBoolean(mIsAccessibilityTool);
         parcel.writeString(mTileServiceClassName);
+        parcel.writeInt(mIntroResId);
     }
 
     private void initFromParcel(Parcel parcel) {
@@ -1137,6 +1171,7 @@
         mNonLocalizedDescription = parcel.readString();
         mIsAccessibilityTool = parcel.readBoolean();
         mTileServiceClassName = parcel.readString();
+        mIntroResId = parcel.readInt();
     }
 
     @Override
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index 52a1cad..9a73219 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -70,6 +70,11 @@
     private final ActivityInfo mActivityInfo;
 
     /**
+     * Resource id of the intro of the accessibility shortcut target.
+     */
+    private final int mIntroResId;
+
+    /**
      * Resource id of the summary of the accessibility shortcut target.
      */
     private final int mSummaryResId;
@@ -160,6 +165,9 @@
             // Get tile service class name
             mTileServiceClassName = asAttributes.getString(
                     com.android.internal.R.styleable.AccessibilityShortcutTarget_tileService);
+            // Gets intro
+            mIntroResId = asAttributes.getResourceId(
+                    com.android.internal.R.styleable.AccessibilityShortcutTarget_intro, 0);
             asAttributes.recycle();
 
             if ((mDescriptionResId == 0 && mHtmlDescriptionRes == 0) || mSummaryResId == 0) {
@@ -203,6 +211,16 @@
     }
 
     /**
+     * The localized intro of the accessibility shortcut target.
+     *
+     * @return The localized intro.
+     */
+    @Nullable
+    public String loadIntro(@NonNull PackageManager packageManager) {
+        return loadResourceString(packageManager, mActivityInfo, mIntroResId);
+    }
+
+    /**
      * The localized description of the accessibility shortcut target.
      *
      * @return The localized description.
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index a58ceaa..294621e 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -779,6 +779,16 @@
          * @param started {@code true} if the process transits from non-FGS state to FGS state.
          */
         void onForegroundServiceStateChanged(String packageName, int uid, int pid, boolean started);
+
+        /**
+         * Call when the notification of the foreground service is updated.
+         *
+         * @param packageName The package name of the process.
+         * @param uid The UID of the process.
+         * @param foregroundId The current foreground service notification ID, a negative value
+         *                     means this notification is being removed.
+         */
+        void onForegroundServiceNotificationUpdated(String packageName, int uid, int foregroundId);
     }
 
     /**
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index acbcb3e..5012121 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1121,7 +1121,7 @@
 
         mPackageName = opts.getString(KEY_PACKAGE_NAME);
         try {
-            mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT);
+            mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT, PendingIntent.class);
         } catch (RuntimeException e) {
             Slog.w(TAG, e);
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ec2115c..a3dd705a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2900,6 +2900,14 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public int getAssociatedDisplayId()  {
+        return isAssociatedWithDisplay() ? getDisplayId() : Display.INVALID_DISPLAY;
+    }
+
     @Override
     public Display getDisplayNoVerify() {
         if (mDisplay == null) {
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index cc8b182..490afc1 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -128,6 +128,7 @@
             int callingUid, in Intent intent, in String resolvedType,
             in IVoiceInteractionSession session, in IVoiceInteractor interactor, int flags,
             in ProfilerInfo profilerInfo, in Bundle options, int userId);
+    String getVoiceInteractorPackageName(in IBinder callingVoiceInteractor);
     int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid,
             int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY)")
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 5d1f4df..9910000 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -591,7 +591,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public boolean isDeviceLocked(int userId) {
         try {
-            return mTrustManager.isDeviceLocked(userId);
+            return mTrustManager.isDeviceLocked(userId, mContext.getAssociatedDisplayId());
         } catch (RemoteException e) {
             return false;
         }
@@ -617,7 +617,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isDeviceSecure(int userId) {
         try {
-            return mTrustManager.isDeviceSecure(userId);
+            return mTrustManager.isDeviceSecure(userId, mContext.getAssociatedDisplayId());
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index 358ce6a..18343fd 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -18,7 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.TestApi;
+import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -43,6 +45,9 @@
         private Rational mAspectRatio;
 
         @Nullable
+        private Rational mExpandedAspectRatio;
+
+        @Nullable
         private List<RemoteAction> mUserActions;
 
         @Nullable
@@ -67,6 +72,24 @@
         }
 
         /**
+         * Sets the aspect ratio for the expanded picture-in-picture mode. The aspect ratio is
+         * defined as the desired width / height. <br/>
+         * The aspect ratio cannot be changed from horizontal to vertical or vertical to horizontal
+         * while the PIP is shown. Any such changes will be ignored. <br/>
+         *
+         * Setting the expanded ratio shows the activity's support for expanded mode.
+         *
+         * @param expandedAspectRatio must not be between 2.39:1 and 1:2.39 (inclusive). If {@code
+         *                            null}, expanded picture-in-picture mode is not supported.
+         * @return this builder instance.
+         */
+        @RequiresFeature(PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE)
+        public @NonNull Builder setExpandedAspectRatio(@Nullable Rational expandedAspectRatio) {
+            mExpandedAspectRatio = expandedAspectRatio;
+            return this;
+        }
+
+        /**
          * Sets the user actions.  If there are more than
          * {@link Activity#getMaxNumPictureInPictureActions()} actions, then the input list
          * will be truncated to that number.
@@ -152,7 +175,8 @@
          * @see Activity#setPictureInPictureParams(PictureInPictureParams)
          */
         public PictureInPictureParams build() {
-            PictureInPictureParams params = new PictureInPictureParams(mAspectRatio, mUserActions,
+            PictureInPictureParams params = new PictureInPictureParams(mAspectRatio,
+                    mExpandedAspectRatio, mUserActions,
                     mSourceRectHint, mAutoEnterEnabled, mSeamlessResizeEnabled);
             return params;
         }
@@ -165,6 +189,12 @@
     private Rational mAspectRatio;
 
     /**
+     * The expected aspect ratio of the vertically expanded picture-in-picture window.
+     */
+    @Nullable
+    private Rational mExpandedAspectRatio;
+
+    /**
      * The set of actions that are associated with this activity when in picture-in-picture.
      */
     @Nullable
@@ -197,9 +227,8 @@
 
     /** {@hide} */
     PictureInPictureParams(Parcel in) {
-        if (in.readInt() != 0) {
-            mAspectRatio = new Rational(in.readInt(), in.readInt());
-        }
+        mAspectRatio = readRationalFromParcel(in);
+        mExpandedAspectRatio = readRationalFromParcel(in);
         if (in.readInt() != 0) {
             mUserActions = new ArrayList<>();
             in.readTypedList(mUserActions, RemoteAction.CREATOR);
@@ -216,9 +245,11 @@
     }
 
     /** {@hide} */
-    PictureInPictureParams(Rational aspectRatio, List<RemoteAction> actions,
-            Rect sourceRectHint, Boolean autoEnterEnabled, Boolean seamlessResizeEnabled) {
+    PictureInPictureParams(Rational aspectRatio, Rational expandedAspectRatio,
+            List<RemoteAction> actions, Rect sourceRectHint, Boolean autoEnterEnabled,
+            Boolean seamlessResizeEnabled) {
         mAspectRatio = aspectRatio;
+        mExpandedAspectRatio = expandedAspectRatio;
         mUserActions = actions;
         mSourceRectHint = sourceRectHint;
         mAutoEnterEnabled = autoEnterEnabled;
@@ -230,7 +261,7 @@
      * @hide
      */
     public PictureInPictureParams(PictureInPictureParams other) {
-        this(other.mAspectRatio, other.mUserActions,
+        this(other.mAspectRatio, other.mExpandedAspectRatio, other.mUserActions,
                 other.hasSourceBoundsHint() ? new Rect(other.getSourceRectHint()) : null,
                 other.mAutoEnterEnabled, other.mSeamlessResizeEnabled);
     }
@@ -243,6 +274,10 @@
         if (otherArgs.hasSetAspectRatio()) {
             mAspectRatio = otherArgs.mAspectRatio;
         }
+
+        // Copy either way because null can be used to explicitly unset the value
+        mExpandedAspectRatio = otherArgs.mExpandedAspectRatio;
+
         if (otherArgs.hasSetActions()) {
             mUserActions = otherArgs.mUserActions;
         }
@@ -283,6 +318,26 @@
     }
 
     /**
+     * @return the expanded aspect ratio. If none is set, return 0.
+     * @hide
+     */
+    @TestApi
+    public float getExpandedAspectRatio() {
+        if (mExpandedAspectRatio != null) {
+            return mExpandedAspectRatio.floatValue();
+        }
+        return 0f;
+    }
+
+    /**
+     * @return whether the expanded aspect ratio is set
+     * @hide
+     */
+    public boolean hasSetExpandedAspectRatio() {
+        return mExpandedAspectRatio != null;
+    }
+
+    /**
      * @return the set of user actions.
      * @hide
      */
@@ -349,7 +404,8 @@
      */
     public boolean empty() {
         return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio()
-                && mAutoEnterEnabled != null && mSeamlessResizeEnabled != null;
+                && !hasSetExpandedAspectRatio() && mAutoEnterEnabled != null
+                && mSeamlessResizeEnabled != null;
     }
 
     @Override
@@ -360,13 +416,14 @@
         return Objects.equals(mAutoEnterEnabled, that.mAutoEnterEnabled)
                 && Objects.equals(mSeamlessResizeEnabled, that.mSeamlessResizeEnabled)
                 && Objects.equals(mAspectRatio, that.mAspectRatio)
+                && Objects.equals(mExpandedAspectRatio, that.mExpandedAspectRatio)
                 && Objects.equals(mUserActions, that.mUserActions)
                 && Objects.equals(mSourceRectHint, that.mSourceRectHint);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mAspectRatio, mUserActions, mSourceRectHint,
+        return Objects.hash(mAspectRatio, mExpandedAspectRatio, mUserActions, mSourceRectHint,
                 mAutoEnterEnabled, mSeamlessResizeEnabled);
     }
 
@@ -377,13 +434,8 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        if (mAspectRatio != null) {
-            out.writeInt(1);
-            out.writeInt(mAspectRatio.getNumerator());
-            out.writeInt(mAspectRatio.getDenominator());
-        } else {
-            out.writeInt(0);
-        }
+        writeRationalToParcel(mAspectRatio, out);
+        writeRationalToParcel(mExpandedAspectRatio, out);
         if (mUserActions != null) {
             out.writeInt(1);
             out.writeTypedList(mUserActions, 0);
@@ -410,10 +462,28 @@
         }
     }
 
+    private void writeRationalToParcel(Rational rational, Parcel out) {
+        if (rational != null) {
+            out.writeInt(1);
+            out.writeInt(rational.getNumerator());
+            out.writeInt(rational.getDenominator());
+        } else {
+            out.writeInt(0);
+        }
+    }
+
+    private Rational readRationalFromParcel(Parcel in) {
+        if (in.readInt() != 0) {
+            return new Rational(in.readInt(), in.readInt());
+        }
+        return null;
+    }
+
     @Override
     public String toString() {
         return "PictureInPictureParams("
                 + " aspectRatio=" + getAspectRatioRational()
+                + " expandedAspectRatio=" + mExpandedAspectRatio
                 + " sourceRectHint=" + getSourceRectHint()
                 + " hasSetActions=" + hasSetActions()
                 + " isAutoPipEnabled=" + isAutoEnterEnabled()
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index 433b275..7014d69 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -1168,6 +1168,23 @@
         }
     }
 
+    /**
+     * @return the package name of the service providing the VoiceInteractionService.
+     */
+    @NonNull
+    public String getPackageName() {
+        String packageName = null;
+        if (mActivity != null && mInteractor != null) {
+            try {
+                packageName = ActivityTaskManager.getService()
+                    .getVoiceInteractorPackageName(mInteractor.asBinder());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return packageName == null ? "" : packageName;
+    }
+
     void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
         String innerPrefix = prefix + "    ";
         if (mActiveRequests.size() > 0) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6614be7..9a7093e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8307,10 +8307,10 @@
      * @hide
      */
     @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
-    public void reportPasswordChanged(@UserIdInt int userId) {
+    public void reportPasswordChanged(PasswordMetrics metrics, @UserIdInt int userId) {
         if (mService != null) {
             try {
-                mService.reportPasswordChanged(userId);
+                mService.reportPasswordChanged(metrics, userId);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index eedc042..0b9d51f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -158,7 +158,7 @@
     void forceRemoveActiveAdmin(in ComponentName policyReceiver, int userHandle);
     boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle);
 
-    void reportPasswordChanged(int userId);
+    void reportPasswordChanged(in PasswordMetrics metrics, int userId);
     void reportFailedPasswordAttempt(int userHandle);
     void reportSuccessfulPasswordAttempt(int userHandle);
     void reportFailedBiometricAttempt(int userHandle);
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 8c59982..b170aa2 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -16,8 +16,12 @@
 
 package android.app.admin;
 
+import android.Manifest;
 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.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
@@ -86,7 +90,12 @@
             TAG_KEY_INTEGRITY_VIOLATION,
             TAG_CERT_VALIDATION_FAILURE,
             TAG_CAMERA_POLICY_SET,
-            TAG_PASSWORD_COMPLEXITY_REQUIRED
+            TAG_PASSWORD_COMPLEXITY_REQUIRED,
+            TAG_PASSWORD_CHANGED,
+            TAG_WIFI_CONNECTION,
+            TAG_WIFI_DISCONNECTION,
+            TAG_BLUETOOTH_CONNECTION,
+            TAG_BLUETOOTH_DISCONNECTION,
     })
     public @interface SecurityLogTag {}
 
@@ -495,6 +504,65 @@
             SecurityLogTags.SECURITY_PASSWORD_COMPLEXITY_REQUIRED;
 
     /**
+     * Indicates that a user has just changed their lockscreen password.
+     * The log entry contains the following information about the
+     * event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] complexity for the new password ({@code Integer})
+     * <li> [1] target user ID ({@code Integer})
+     *
+     * <p>Password complexity levels are defined as in
+     * {@link DevicePolicyManager#getPasswordComplexity()}
+     */
+    public static final int TAG_PASSWORD_CHANGED = SecurityLogTags.SECURITY_PASSWORD_CHANGED;
+
+    /**
+     * Indicates that the device attempts to connect to a WiFi network.
+     * The log entry contains the following information about the
+     * event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] The SSID of the network ({@code String})
+     * <li> [1] The BSSID of the network ({@code String})
+     * <li> [2] Whether the connection is successful ({@code Integer}, 1 if successful, 0 otherwise)
+     * <li> [3] Optional human-readable failure reason, empty string if none ({@code String})
+     */
+    public static final int TAG_WIFI_CONNECTION = SecurityLogTags.SECURITY_WIFI_CONNECTION;
+
+    /**
+     * Indicates that the device disconnects from a connected WiFi network.
+     * The log entry contains the following information about the
+     * event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] The SSID of the connected network ({@code String})
+     * <li> [1] The BSSID of the connected network ({@code String})
+     * <li> [2] Optional human-readable disconnection reason, empty string if none ({@code String})
+     */
+    public static final int TAG_WIFI_DISCONNECTION = SecurityLogTags.SECURITY_WIFI_DISCONNECTION;
+
+    /**
+     * Indicates that the device attempts to connect to a Bluetooth device.
+     * The log entry contains the following information about the
+     * event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] The MAC address of the Bluetooth device ({@code String})
+     * <li> [1] Whether the connection is successful ({@code Integer}, 1 if successful, 0 otherwise)
+     * <li> [2] Optional human-readable failure reason, empty string if none ({@code String})
+     */
+    public static final int TAG_BLUETOOTH_CONNECTION =
+            SecurityLogTags.SECURITY_BLUETOOTH_CONNECTION;
+
+    /**
+     * Indicates that the device disconnects from a connected Bluetooth device.
+     * The log entry contains the following information about the
+     * event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] The MAC address of the connected Bluetooth device ({@code String})
+     * <li> [1] Optional human-readable disconnection reason, empty string if none ({@code String})
+     */
+    public static final int TAG_BLUETOOTH_DISCONNECTION =
+            SecurityLogTags.SECURITY_BLUETOOTH_DISCONNECTION;
+
+    /**
      * Event severity level indicating that the event corresponds to normal workflow.
      */
     public static final int LEVEL_INFO = 1;
@@ -635,6 +703,7 @@
                 case TAG_USER_RESTRICTION_REMOVED:
                 case TAG_CAMERA_POLICY_SET:
                 case TAG_PASSWORD_COMPLEXITY_REQUIRED:
+                case TAG_PASSWORD_CHANGED:
                     return LEVEL_INFO;
                 case TAG_CERT_AUTHORITY_REMOVED:
                 case TAG_CRYPTO_SELF_TEST_COMPLETED:
@@ -725,6 +794,13 @@
                         return null;
                     }
                     break;
+                case SecurityLog.TAG_PASSWORD_CHANGED:
+                    try {
+                        userId = getIntegerData(1);
+                    } catch (Exception e) {
+                        return null;
+                    }
+                    break;
                 default:
                     userId = UserHandle.USER_NULL;
             }
@@ -840,15 +916,21 @@
             throws IOException;
 
     /**
-     * Write a log entry to the underlying storage, with a string payload.
-     * @hide
-     */
-    public static native int writeEvent(int tag, String str);
-
-    /**
      * Write a log entry to the underlying storage, with several payloads.
      * Supported types of payload are: integer, long, float, string plus array of supported types.
+     *
+     * <p>Security log is part of Android's device management capability that tracks
+     * security-sensitive events for auditing purposes.
+     *
+     * @param tag the tag ID of the security event
+     * @param payloads a list of payload values. Each tag dictates the expected payload types
+     *                 and their meanings
+     * @see DevicePolicyManager#setSecurityLoggingEnabled(ComponentName, boolean)
+     *
      * @hide
      */
-    public static native int writeEvent(int tag, Object... payloads);
+    // TODO(b/218658622): enforce WRITE_SECURITY_LOG in logd.
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_SECURITY_LOG)
+    public static native int writeEvent(@SecurityLogTag int tag, @NonNull Object... payloads);
 }
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index db5245c..5f41109 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -40,3 +40,8 @@
 210033 security_cert_validation_failure         (reason|3)
 210034 security_camera_policy_set               (package|3),(admin_user|1),(target_user|1),(disabled|1)
 210035 security_password_complexity_required    (package|3),(admin_user|1),(target_user|1),(complexity|1)
+210036 security_password_changed                (password_complexity|1),(target_user|1)
+210037 security_wifi_connection                 (ssid|3),(bssid|3),(success|1),(reason|3)
+210038 security_wifi_disconnection              (ssid|3),(bssid|3),(reason|3)
+210039 security_bluetooth_connection            (addr|3),(success|1),(reason|3)
+210040 security_bluetooth_disconnection         (addr|3),(reason|3)
\ No newline at end of file
diff --git a/core/java/android/app/cloudsearch/SearchRequest.java b/core/java/android/app/cloudsearch/SearchRequest.java
index 0c5c30c..ef66c0c 100644
--- a/core/java/android/app/cloudsearch/SearchRequest.java
+++ b/core/java/android/app/cloudsearch/SearchRequest.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.StringDef;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -91,6 +92,14 @@
     @NonNull
     private Bundle mSearchConstraints;
 
+    /** Auto set by system servier, and the caller cannot set it.
+     *
+     * The caller's package name.
+     *
+     */
+    @NonNull
+    private String mSource;
+
     private SearchRequest(Parcel in) {
         this.mQuery = in.readString();
         this.mResultOffset = in.readInt();
@@ -98,15 +107,17 @@
         this.mMaxLatencyMillis = in.readFloat();
         this.mSearchConstraints = in.readBundle();
         this.mId = in.readString();
+        this.mSource = in.readString();
     }
 
     private SearchRequest(String query, int resultOffset, int resultNumber, float maxLatencyMillis,
-            Bundle searchConstraints) {
+            Bundle searchConstraints, String source) {
         mQuery = query;
         mResultOffset = resultOffset;
         mResultNumber = resultNumber;
         mMaxLatencyMillis = maxLatencyMillis;
         mSearchConstraints = searchConstraints;
+        mSource = source;
     }
 
     /** Returns the original query. */
@@ -136,35 +147,37 @@
         return mSearchConstraints;
     }
 
+    /** Gets the caller's package name. */
+    @NonNull
+    public String getSource() {
+        return mSource;
+    }
+
     /** Returns the search request id, which is used to identify the request. */
     @NonNull
     public String getRequestId() {
         if (mId == null || mId.length() == 0) {
-            boolean isPresubmit =
-                    mSearchConstraints.containsKey(CONSTRAINT_IS_PRESUBMIT_SUGGESTION)
-                    && mSearchConstraints.getBoolean(CONSTRAINT_IS_PRESUBMIT_SUGGESTION);
-
-            String searchProvider = "EMPTY";
-            if (mSearchConstraints.containsKey(CONSTRAINT_SEARCH_PROVIDER_FILTER)) {
-                searchProvider = mSearchConstraints.getString(CONSTRAINT_SEARCH_PROVIDER_FILTER);
-            }
-
-            String rawContent = String.format("%s\t%d\t%d\t%f\t%b\t%s",
-                    mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis,
-                    isPresubmit, searchProvider);
-
-            mId = String.valueOf(rawContent.hashCode());
+            mId = String.valueOf(toString().hashCode());
         }
 
         return mId;
     }
 
+    /** Sets the caller, and this will be set by the system server.
+     *
+     * @hide
+     */
+    public void setSource(@NonNull String source) {
+        this.mSource = source;
+    }
+
     private SearchRequest(Builder b) {
         mQuery = requireNonNull(b.mQuery);
         mResultOffset = b.mResultOffset;
         mResultNumber = b.mResultNumber;
         mMaxLatencyMillis = b.mMaxLatencyMillis;
         mSearchConstraints = requireNonNull(b.mSearchConstraints);
+        mSource = requireNonNull(b.mSource);
     }
 
     /**
@@ -192,6 +205,7 @@
         dest.writeFloat(this.mMaxLatencyMillis);
         dest.writeBundle(this.mSearchConstraints);
         dest.writeString(getRequestId());
+        dest.writeString(this.mSource);
     }
 
     @Override
@@ -214,13 +228,30 @@
                 && mResultOffset == that.mResultOffset
                 && mResultNumber == that.mResultNumber
                 && mMaxLatencyMillis == that.mMaxLatencyMillis
-                && Objects.equals(mSearchConstraints, that.mSearchConstraints);
+                && Objects.equals(mSearchConstraints, that.mSearchConstraints)
+                && Objects.equals(mSource, that.mSource);
+    }
+
+    @Override
+    public String toString() {
+        boolean isPresubmit =
+                mSearchConstraints.containsKey(CONSTRAINT_IS_PRESUBMIT_SUGGESTION)
+                        && mSearchConstraints.getBoolean(CONSTRAINT_IS_PRESUBMIT_SUGGESTION);
+
+        String searchProvider = "EMPTY";
+        if (mSearchConstraints.containsKey(CONSTRAINT_SEARCH_PROVIDER_FILTER)) {
+            searchProvider = mSearchConstraints.getString(CONSTRAINT_SEARCH_PROVIDER_FILTER);
+        }
+
+        return String.format("SearchRequest: {query:%s,offset:%d;number:%d;max_latency:%f;"
+                        + "is_presubmit:%b;search_provider:%s;source:%s}", mQuery, mResultOffset,
+                mResultNumber, mMaxLatencyMillis, isPresubmit, searchProvider, mSource);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis,
-                mSearchConstraints);
+                mSearchConstraints, mSource);
     }
 
     /**
@@ -235,6 +266,7 @@
         private int mResultNumber;
         private float mMaxLatencyMillis;
         private Bundle mSearchConstraints;
+        private String mSource;
 
         /**
          *
@@ -250,6 +282,7 @@
             mResultNumber = 10;
             mMaxLatencyMillis = 200;
             mSearchConstraints = Bundle.EMPTY;
+            mSource = "DEFAULT_CALLER";
         }
 
         /** Sets the input query. */
@@ -288,6 +321,17 @@
             return this;
         }
 
+        /** Sets the caller, and this will be set by the system server.
+         *
+         * @hide
+         */
+        @NonNull
+        @TestApi
+        public Builder setSource(@NonNull String source) {
+            this.mSource = source;
+            return this;
+        }
+
         /** Builds a SearchRequest based-on the given params. */
         @NonNull
         public SearchRequest build() {
@@ -297,7 +341,7 @@
             }
 
             return new SearchRequest(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis,
-                               mSearchConstraints);
+                               mSearchConstraints, mSource);
         }
     }
 }
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index edabccf..b786444 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -33,8 +33,8 @@
     void unregisterTrustListener(in ITrustListener trustListener);
     void reportKeyguardShowingChanged();
     void setDeviceLockedForUser(int userId, boolean locked);
-    boolean isDeviceLocked(int userId);
-    boolean isDeviceSecure(int userId);
+    boolean isDeviceLocked(int userId, int displayId);
+    boolean isDeviceSecure(int userId, int displayId);
     boolean isTrustUsuallyManaged(int userId);
     void unlockedByBiometricForUser(int userId, in BiometricSourceType source);
     void clearAllBiometricRecognized(in BiometricSourceType target, int unlockedUser);
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 0f5cd4e..3c256ad 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -1037,6 +1037,7 @@
         // Data can be too large for a transact. Write the data as a Blob, which will be written to
         // ashmem if too large.
         dest.writeBlob(data.marshall());
+        data.recycle();
     }
 
     public static final @android.annotation.NonNull Creator<UsageEvents> CREATOR = new Creator<UsageEvents>() {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index fdff27f..f1abb05 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -16,10 +16,11 @@
 
 package android.companion.virtual;
 
+import android.annotation.CallbackExecutor;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.Activity;
@@ -31,6 +32,7 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.VirtualDisplayFlag;
 import android.hardware.display.VirtualDisplay;
 import android.hardware.display.VirtualDisplayConfig;
 import android.hardware.input.VirtualKeyboard;
@@ -46,6 +48,7 @@
 import android.util.ArrayMap;
 import android.view.Surface;
 
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -210,25 +213,22 @@
          * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC VIRTUAL_DISPLAY_FLAG_PUBLIC} and
          * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
          * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
+         * @param executor The executor on which {@code callback} will be invoked. This is ignored
+         * if {@code callback} is {@code null}.
          * @param callback Callback to call when the state of the {@link VirtualDisplay} changes
-         * @param handler The handler on which the listener should be invoked, or null
-         * if the listener should be invoked on the calling thread's looper.
          * @return The newly created virtual display, or {@code null} if the application could
          * not create the virtual display.
          *
          * @see DisplayManager#createVirtualDisplay
          */
-        // Suppress "ExecutorRegistration" because DisplayManager.createVirtualDisplay takes a
-        // handler
-        @SuppressLint("ExecutorRegistration")
         @Nullable
         public VirtualDisplay createVirtualDisplay(
-                int width,
-                int height,
-                int densityDpi,
+                @IntRange(from = 1) int width,
+                @IntRange(from = 1) int height,
+                @IntRange(from = 1) int densityDpi,
                 @Nullable Surface surface,
-                int flags,
-                @Nullable Handler handler,
+                @VirtualDisplayFlag int flags,
+                @NonNull @CallbackExecutor Executor executor,
                 @Nullable VirtualDisplay.Callback callback) {
             // TODO(b/205343547): Handle display groups properly instead of creating a new display
             //  group for every new virtual display created using this API.
@@ -244,7 +244,7 @@
                             .setFlags(getVirtualDisplayFlags(flags))
                             .build(),
                     callback,
-                    handler);
+                    Objects.requireNonNull(executor));
         }
 
         /**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 957cb24c..0fed733 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7075,6 +7075,18 @@
     public abstract int getDisplayId();
 
     /**
+     * @return Returns the id of the Display object associated with this Context or
+     * {@link Display#INVALID_DISPLAY} if no Display has been associated.
+     * @see #getDisplay()
+     * @see #getDisplayId()
+     *
+     * @hide
+     */
+    public int getAssociatedDisplayId() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * @hide
      */
     @SuppressWarnings("HiddenAbstractMethod")
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4279d07..dc79ee6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3797,6 +3797,16 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports expanded picture-in-picture multi-window mode.
+     *
+     * @see android.app.PictureInPictureParams.Builder#setExpandedAspectRatio
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_EXPANDED_PICTURE_IN_PICTURE
+            = "android.software.expanded_picture_in_picture";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
      * The device supports running activities on secondary displays.
      */
     @SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 8e8aaf1..70b90e6 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -767,7 +767,7 @@
      * order to make sure shortcuts exist and are up-to-date, without the need to explicitly handle
      * the shortcut count limit.
      * @see android.app.NotificationManager#notify(int, Notification)
-     * @see Notification.Builder#setShortcutId(String)
+     * @see android.app.Notification.Builder#setShortcutId(String)
      *
      * <p>If {@link #getMaxShortcutCountPerActivity()} is already reached, an existing shortcut with
      * the lowest rank will be removed to add space for the new shortcut.
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index a06566c..524fe79 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -3455,6 +3455,30 @@
             new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryTenBitOutputStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
 
     /**
+     * <p>An array of mandatory stream combinations which are applicable when device lists
+     * {@code PREVIEW_STABILIZATION} in {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES android.control.availableVideoStabilizationModes}.
+     * This is an app-readable conversion of the maximum resolution mandatory stream combination
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p>
+     * <p>The array of
+     * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
+     * generated according to the documented
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each
+     * device which supports {@code PREVIEW_STABILIZATION}
+     * Clients can use the array as a quick reference to find an appropriate camera stream
+     * combination.
+     * The mandatory stream combination array will be {@code null} in case the device does not
+     * list {@code PREVIEW_STABILIZATION} in {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES android.control.availableVideoStabilizationModes}.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES
+     */
+    @PublicKey
+    @NonNull
+    @SyntheticKey
+    public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_PREVIEW_STABILIZATION_OUTPUT_STREAM_COMBINATIONS =
+            new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryPreviewStabilizationOutputStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
+
+    /**
      * <p>Whether the camera device supports multi-resolution input or output streams</p>
      * <p>A logical multi-camera or an ultra high resolution camera may support multi-resolution
      * input or output streams. With multi-resolution output streams, the camera device is able
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 1a42eaf..8f42b1f 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -883,6 +883,27 @@
      * </table><br>
      * </p>
      *
+     *<p> For devices where {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES}
+     * includes {@link CameraMetadata#CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION},
+     * the following stream combinations are guaranteed,
+     * for CaptureRequests where {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE} is set to
+     * {@link CameraMetadata#CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION} <p>
+     * <table>
+     * <tr><th colspan="7">Preview stabilization guaranteed stream configurations</th></tr>
+     * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr>
+     * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+     * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code RECORD}</td><td colspan="4" id="rb"></td> <td>Stabilized preview, GPU video processing, or no-preview stabilized video recording.</td> </tr>
+     * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG / YUV}</td><td id="rb">{@code MAXIMUM }</td><td>Standard still imaging with stabilized preview.</td> </tr>
+     * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV / YUV}</td><td id="rb">{@code RECORD }</td><td>High-resolution recording with stabilized preview and recording stream.</td> </tr>
+     * </table><br>
+     * <p>
+     * For the maximum size column, PREVIEW refers to the best size match to the device's screen
+     * resolution, or to 1080p (1920x1080), whichever is smaller. RECORD refers to the camera
+     * device's maximum supported recording resolution, as determined by
+     * {@link android.media.CamcorderProfile}. MAXIMUM refers to the camera device's maximum output
+     * resolution for that format or target from {@link StreamConfigurationMap#getOutputSizes(int)}.
+     * </p>
+     *
      * <p>Since the capabilities of camera devices vary greatly, a given camera device may support
      * target combinations with sizes outside of these guarantees, but this can only be tested for
      * by calling {@link #isSessionConfigurationSupported} or attempting to create a session with
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 9b67633..4fb496d 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -334,6 +334,7 @@
     private static final int MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT = 2;
     private static final int MANDATORY_STREAM_CONFIGURATIONS_10BIT = 3;
     private static final int MANDATORY_STREAM_CONFIGURATIONS_USE_CASE = 4;
+    private static final int MANDATORY_STREAM_CONFIGURATIONS_PREVIEW_STABILIZATION = 5;
 
     private static String translateLocationProviderToProcess(final String provider) {
         if (provider == null) {
@@ -709,6 +710,15 @@
                         return (T) metadata.getMandatoryUseCaseStreamCombinations();
                     }
                 });
+        sGetCommandMap.put(
+                CameraCharacteristics.SCALER_MANDATORY_PREVIEW_STABILIZATION_OUTPUT_STREAM_COMBINATIONS.getNativeKey(),
+                new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getMandatoryPreviewStabilizationStreamCombinations();
+                    }
+                });
 
         sGetCommandMap.put(
                 CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
@@ -1400,6 +1410,24 @@
                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
     }
 
+    private boolean isPreviewStabilizationSupported() {
+        boolean ret = false;
+
+        int[] videoStabilizationModes =
+                getBase(CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES);
+        if (videoStabilizationModes == null) {
+            return false;
+        }
+        for (int mode : videoStabilizationModes) {
+            if (mode == CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION) {
+                ret = true;
+                break;
+            }
+        }
+
+        return ret;
+    }
+
     private MandatoryStreamCombination[] getMandatoryStreamCombinationsHelper(
             int mandatoryStreamsType) {
         int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
@@ -1411,7 +1439,7 @@
         int hwLevel = getBase(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
         MandatoryStreamCombination.Builder build = new MandatoryStreamCombination.Builder(
                 mCameraId, hwLevel, mDisplaySize, caps, getStreamConfigurationMap(),
-                getStreamConfigurationMapMaximumResolution());
+                getStreamConfigurationMapMaximumResolution(), isPreviewStabilizationSupported());
 
         List<MandatoryStreamCombination> combs = null;
         switch (mandatoryStreamsType) {
@@ -1427,6 +1455,9 @@
             case MANDATORY_STREAM_CONFIGURATIONS_USE_CASE:
                 combs = build.getAvailableMandatoryStreamUseCaseCombinations();
                 break;
+            case MANDATORY_STREAM_CONFIGURATIONS_PREVIEW_STABILIZATION:
+                combs = build.getAvailableMandatoryPreviewStabilizedStreamCombinations();
+                break;
             default:
                 combs = build.getAvailableMandatoryStreamCombinations();
         }
@@ -1464,6 +1495,11 @@
         return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_USE_CASE);
     }
 
+    private MandatoryStreamCombination[] getMandatoryPreviewStabilizationStreamCombinations() {
+        return getMandatoryStreamCombinationsHelper(
+                MANDATORY_STREAM_CONFIGURATIONS_PREVIEW_STABILIZATION);
+    }
+
     private StreamConfigurationMap getStreamConfigurationMap() {
         StreamConfiguration[] configurations = getBase(
                 CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 0d93c98..8c0dcfc 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -1262,6 +1262,50 @@
                 "Preview, in-application image processing, and YUV still image capture"),
     };
 
+    private static StreamCombinationTemplate sPreviewStabilizedStreamCombinations[] = {
+        // 1 stream combinations
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
+                "Stabilized preview, GPU video processing, or no-preview stabilized recording"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
+                "Stabilized preview, GPU video processing, or no-preview stabilized recording"),
+        //2 stream combinations
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+                "Standard JPEG still imaging with stabilized preview"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+                "Standard YUV still imaging with stabilized preview"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
+                "Standard YUV still imaging with stabilized in-app image processing stream"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
+                "Standard JPEG still imaging with stabilized in-app image processing stream"),
+
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+                "High-resolution video recording with preview both streams stabilized"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
+                "High-resolution video recording with preview both streams stabilized"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD),
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
+                "High-resolution video recording with preview both streams stabilized"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD),
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+                "High-resolution video recording with preview both streams stabilized"),
+    };
+
     /**
      * Helper builder class to generate a list of available mandatory stream combinations.
      * @hide
@@ -1273,6 +1317,7 @@
         private StreamConfigurationMap mStreamConfigMap;
         private StreamConfigurationMap mStreamConfigMapMaximumResolution;
         private boolean mIsHiddenPhysicalCamera;
+        private boolean mIsPreviewStabilizationSupported;
 
         private final Size kPreviewSizeBound = new Size(1920, 1088);
 
@@ -1289,7 +1334,7 @@
          */
         public Builder(int cameraId, int hwLevel, @NonNull Size displaySize,
                 @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm,
-                StreamConfigurationMap smMaxResolution) {
+                StreamConfigurationMap smMaxResolution, boolean previewStabilization) {
             mCameraId = cameraId;
             mDisplaySize = displaySize;
             mCapabilities = capabilities;
@@ -1298,24 +1343,12 @@
             mHwLevel = hwLevel;
             mIsHiddenPhysicalCamera =
                     CameraManager.isHiddenPhysicalCamera(Integer.toString(mCameraId));
+            mIsPreviewStabilizationSupported = previewStabilization;
         }
 
-        /**
-         * Retrieve a list of all available mandatory 10-bit output capable stream combinations.
-         *
-         * @return a non-modifiable list of supported mandatory 10-bit capable stream combinations,
-         *         null in case device is not 10-bit output capable.
-         */
-        public @NonNull List<MandatoryStreamCombination>
-        getAvailableMandatory10BitStreamCombinations() {
-            // Since 10-bit streaming support is optional, we mandate these stream
-            // combinations regardless of camera device capabilities.
-
-            StreamCombinationTemplate []chosenStreamCombinations = s10BitOutputStreamCombinations;
-            if (!is10BitOutputSupported()) {
-                Log.v(TAG, "Device is not able to output 10-bit!");
-                return null;
-            }
+        private @Nullable List<MandatoryStreamCombination>
+        getAvailableMandatoryStreamCombinationsInternal(
+                StreamCombinationTemplate []chosenStreamCombinations, boolean s10Bit) {
 
             HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
                     enumerateAvailableSizes();
@@ -1334,7 +1367,7 @@
                     Pair<SizeThreshold, Integer> pair;
                     pair = new Pair<>(template.mSizeThreshold, new Integer(template.mFormat));
                     sizes = availableSizes.get(pair);
-                    if (template.mFormat == ImageFormat.YCBCR_P010) {
+                    if (s10Bit && template.mFormat == ImageFormat.YCBCR_P010) {
                         // Make sure that exactly the same 10 and 8-bit YUV streams sizes are
                         // supported
                         pair = new Pair<>(template.mSizeThreshold,
@@ -1354,7 +1387,8 @@
                         streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
                                 isMaximumSize, /*isInput*/ false,
                                 /*isUltraHighResolution*/ false,
-                                /*is10BitCapable*/ template.mFormat != ImageFormat.JPEG);
+                                /*is10BitCapable*/ s10Bit ? template.mFormat != ImageFormat.JPEG :
+                                        false);
                     } catch (IllegalArgumentException e) {
                         Log.e(TAG, "No available sizes found for format: " + template.mFormat +
                                 " size threshold: " + template.mSizeThreshold + " combination: " +
@@ -1381,6 +1415,52 @@
         }
 
         /**
+         * Retrieve a list of all available mandatory stream combinations for devices supporting
+         * preview stabilization.
+         *
+         * @return a non-modifiable list of supported mandatory stream combinations on which
+         *         preview stabilization is supported.,
+         *         null in case device is not 10-bit output capable.
+         */
+        public @Nullable List<MandatoryStreamCombination>
+        getAvailableMandatoryPreviewStabilizedStreamCombinations() {
+            // Since preview stabilization support is optional, we mandate these stream
+            // combinations regardless of camera device capabilities.
+
+            StreamCombinationTemplate []chosenStreamCombinations =
+                    sPreviewStabilizedStreamCombinations;
+
+            if (mIsPreviewStabilizationSupported) {
+                Log.v(TAG, "Device does not support preview stabilization");
+                 return null;
+             }
+
+            return getAvailableMandatoryStreamCombinationsInternal(chosenStreamCombinations,
+                    /*10bit*/false);
+        }
+
+
+        /**
+         * Retrieve a list of all available mandatory 10-bit output capable stream combinations.
+         *
+         * @return a non-modifiable list of supported mandatory 10-bit capable stream combinations,
+         *         null in case device is not 10-bit output capable.
+         */
+        public @Nullable List<MandatoryStreamCombination>
+        getAvailableMandatory10BitStreamCombinations() {
+            // Since 10-bit streaming support is optional, we mandate these stream
+            // combinations regardless of camera device capabilities.
+
+            StreamCombinationTemplate []chosenStreamCombinations = s10BitOutputStreamCombinations;
+            if (!is10BitOutputSupported()) {
+                Log.v(TAG, "Device is not able to output 10-bit!");
+                return null;
+            }
+            return getAvailableMandatoryStreamCombinationsInternal(chosenStreamCombinations,
+                    /*10bit*/true);
+        }
+
+        /**
           * Retrieve a list of all available mandatory stream combinations with stream use cases.
           * when the camera device has {@link
           * CameraMetdata.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE} capability.
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 623f38e..971b61b 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -22,6 +22,7 @@
 import android.Manifest;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -38,6 +39,8 @@
 import android.media.projection.MediaProjection;
 import android.os.Build;
 import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -48,6 +51,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 
 /**
@@ -104,6 +108,25 @@
     public static final String DISPLAY_CATEGORY_PRESENTATION =
             "android.hardware.display.category.PRESENTATION";
 
+    /** @hide **/
+    @IntDef(prefix = "VIRTUAL_DISPLAY_FLAG_", flag = true, value = {
+            VIRTUAL_DISPLAY_FLAG_PUBLIC,
+            VIRTUAL_DISPLAY_FLAG_PRESENTATION,
+            VIRTUAL_DISPLAY_FLAG_SECURE,
+            VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY,
+            VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+            VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD,
+            VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH,
+            VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT,
+            VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL,
+            VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS,
+            VIRTUAL_DISPLAY_FLAG_TRUSTED,
+            VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP,
+            VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VirtualDisplayFlag {}
+
     /**
      * Virtual display flag: Create a public display.
      *
@@ -820,7 +843,11 @@
      * VirtualDisplay.Callback, Handler)
      */
     public VirtualDisplay createVirtualDisplay(@NonNull String name,
-            int width, int height, int densityDpi, @Nullable Surface surface, int flags) {
+            @IntRange(from = 1) int width,
+            @IntRange(from = 1) int height,
+            @IntRange(from = 1) int densityDpi,
+            @Nullable Surface surface,
+            @VirtualDisplayFlag int flags) {
         return createVirtualDisplay(name, width, height, densityDpi, surface, flags, null, null);
     }
 
@@ -868,8 +895,13 @@
      * a virtual display with the specified flags.
      */
     public VirtualDisplay createVirtualDisplay(@NonNull String name,
-            int width, int height, int densityDpi, @Nullable Surface surface, int flags,
-            @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
+            @IntRange(from = 1) int width,
+            @IntRange(from = 1) int height,
+            @IntRange(from = 1) int densityDpi,
+            @Nullable Surface surface,
+            @VirtualDisplayFlag int flags,
+            @Nullable VirtualDisplay.Callback callback,
+            @Nullable Handler handler) {
         final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
                 height, densityDpi);
         builder.setFlags(flags);
@@ -882,9 +914,16 @@
 
     // TODO : Remove this hidden API after remove all callers. (Refer to MultiDisplayService)
     /** @hide */
-    public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection,
-            @NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface,
-            int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
+    public VirtualDisplay createVirtualDisplay(
+            @Nullable MediaProjection projection,
+            @NonNull String name,
+            @IntRange(from = 1) int width,
+            @IntRange(from = 1) int height,
+            @IntRange(from = 1) int densityDpi,
+            @Nullable Surface surface,
+            @VirtualDisplayFlag int flags,
+            @Nullable VirtualDisplay.Callback callback,
+            @Nullable Handler handler,
             @Nullable String uniqueId) {
         final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
                 height, densityDpi);
@@ -904,16 +943,24 @@
             @NonNull VirtualDisplayConfig virtualDisplayConfig,
             @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
             @Nullable Context windowContext) {
+        Executor executor = null;
+        // If callback is null, the executor will not be used. Avoid creating the handler and the
+        // handler executor.
+        if (callback != null) {
+            executor = new HandlerExecutor(
+                    Handler.createAsync(handler != null ? handler.getLooper() : Looper.myLooper()));
+        }
         return mGlobal.createVirtualDisplay(mContext, projection, null /* virtualDevice */,
-                virtualDisplayConfig, callback, handler, windowContext);
+                virtualDisplayConfig, callback, executor, windowContext);
     }
 
     /** @hide */
     public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice,
             @NonNull VirtualDisplayConfig virtualDisplayConfig,
-            @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
+            @Nullable VirtualDisplay.Callback callback,
+            @NonNull Executor executor) {
         return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice,
-                virtualDisplayConfig, callback, handler, null);
+                virtualDisplayConfig, callback, executor, null);
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 7e7a648..889100d 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -56,6 +56,8 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * Manager communication with the display manager service on behalf of
@@ -585,8 +587,9 @@
 
     public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection,
             IVirtualDevice virtualDevice, @NonNull VirtualDisplayConfig virtualDisplayConfig,
-            VirtualDisplay.Callback callback, Handler handler, @Nullable Context windowContext) {
-        VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
+            VirtualDisplay.Callback callback, @Nullable Executor executor,
+            @Nullable Context windowContext) {
+        VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, executor);
         IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
         int displayId;
         try {
@@ -1048,61 +1051,36 @@
     }
 
     private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
-        private VirtualDisplayCallbackDelegate mDelegate;
+        @Nullable private final VirtualDisplay.Callback mCallback;
+        @Nullable private final Executor mExecutor;
 
-        public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) {
-            if (callback != null) {
-                mDelegate = new VirtualDisplayCallbackDelegate(callback, handler);
-            }
+        VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) {
+            mCallback = callback;
+            mExecutor = mCallback != null ? Objects.requireNonNull(executor) : null;
         }
 
+        // These methods are called from the binder thread, but the AIDL is oneway, so it should be
+        // safe to call the callback on arbitrary executors directly without risking blocking
+        // the system.
+
         @Override // Binder call
         public void onPaused() {
-            if (mDelegate != null) {
-                mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED);
+            if (mCallback != null) {
+                mExecutor.execute(mCallback::onPaused);
             }
         }
 
         @Override // Binder call
         public void onResumed() {
-            if (mDelegate != null) {
-                mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED);
+            if (mCallback != null) {
+                mExecutor.execute(mCallback::onResumed);
             }
         }
 
         @Override // Binder call
         public void onStopped() {
-            if (mDelegate != null) {
-                mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED);
-            }
-        }
-    }
-
-    private final static class VirtualDisplayCallbackDelegate extends Handler {
-        public static final int MSG_DISPLAY_PAUSED = 0;
-        public static final int MSG_DISPLAY_RESUMED = 1;
-        public static final int MSG_DISPLAY_STOPPED = 2;
-
-        private final VirtualDisplay.Callback mCallback;
-
-        public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback,
-                Handler handler) {
-            super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
-            mCallback = callback;
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_DISPLAY_PAUSED:
-                    mCallback.onPaused();
-                    break;
-                case MSG_DISPLAY_RESUMED:
-                    mCallback.onResumed();
-                    break;
-                case MSG_DISPLAY_STOPPED:
-                    mCallback.onStopped();
-                    break;
+            if (mCallback != null) {
+                mExecutor.execute(mCallback::onStopped);
             }
         }
     }
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 0e86f43..e292394 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -21,6 +21,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.display.DisplayManager.VirtualDisplayFlag;
 import android.media.projection.MediaProjection;
 import android.os.Handler;
 import android.os.IBinder;
@@ -70,6 +71,7 @@
      * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
      * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
      */
+    @VirtualDisplayFlag
     private int mFlags = 0;
 
     /**
@@ -120,7 +122,7 @@
             @IntRange(from = 1) int width,
             @IntRange(from = 1) int height,
             @IntRange(from = 1) int densityDpi,
-            int flags,
+            @VirtualDisplayFlag int flags,
             @Nullable Surface surface,
             @Nullable String uniqueId,
             int displayIdToMirror,
@@ -141,6 +143,8 @@
                 IntRange.class, null, mDensityDpi,
                 "from", 1);
         this.mFlags = flags;
+        com.android.internal.util.AnnotationValidations.validate(
+                VirtualDisplayFlag.class, null, mFlags);
         this.mSurface = surface;
         this.mUniqueId = uniqueId;
         this.mDisplayIdToMirror = displayIdToMirror;
@@ -190,7 +194,7 @@
      * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
      */
     @DataClass.Generated.Member
-    public int getFlags() {
+    public @VirtualDisplayFlag int getFlags() {
         return mFlags;
     }
 
@@ -291,6 +295,8 @@
                 IntRange.class, null, mDensityDpi,
                 "from", 1);
         this.mFlags = flags;
+        com.android.internal.util.AnnotationValidations.validate(
+                VirtualDisplayFlag.class, null, mFlags);
         this.mSurface = surface;
         this.mUniqueId = uniqueId;
         this.mDisplayIdToMirror = displayIdToMirror;
@@ -324,7 +330,7 @@
         private @IntRange(from = 1) int mWidth;
         private @IntRange(from = 1) int mHeight;
         private @IntRange(from = 1) int mDensityDpi;
-        private int mFlags;
+        private @VirtualDisplayFlag int mFlags;
         private @Nullable Surface mSurface;
         private @Nullable String mUniqueId;
         private int mDisplayIdToMirror;
@@ -419,7 +425,7 @@
          * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
          */
         @DataClass.Generated.Member
-        public @NonNull Builder setFlags(int value) {
+        public @NonNull Builder setFlags(@VirtualDisplayFlag int value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x10;
             mFlags = value;
@@ -517,10 +523,10 @@
     }
 
     @DataClass.Generated(
-            time = 1620657851981L,
+            time = 1643938791506L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java",
-            inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate  int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate  int mDisplayIdToMirror\nprivate @android.annotation.Nullable android.os.IBinder mWindowTokenClientToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
+            inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate @android.hardware.display.DisplayManager.VirtualDisplayFlag int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate  int mDisplayIdToMirror\nprivate @android.annotation.Nullable android.os.IBinder mWindowTokenClientToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index ad3de25..244335d 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -31,7 +31,7 @@
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Set;
-import java.util.function.Supplier;
+import java.util.function.Function;
 
 /**
  * A mapping from String keys to values of various types. In most cases, you
@@ -252,11 +252,10 @@
         if (size == 0) {
             return null;
         }
-        Object o = getValueAt(0);
         try {
-            return (String) o;
-        } catch (ClassCastException e) {
-            typeWarning("getPairValue()", o, "String", e);
+            return getValueAt(0, String.class);
+        } catch (ClassCastException | BadParcelableException e) {
+            typeWarning("getPairValue()", /* value */ null, "String", e);
             return null;
         }
     }
@@ -309,7 +308,7 @@
                 }
                 for (int i = 0, n = mMap.size(); i < n; i++) {
                     // Triggers deserialization of i-th item, if needed
-                    getValueAt(i);
+                    getValueAt(i, /* clazz */ null);
                 }
             }
         }
@@ -324,8 +323,21 @@
      * @hide
      */
     final Object getValue(String key) {
+        return getValue(key, /* clazz */ null);
+    }
+
+    /**
+     * Returns the value for key {@code key} for expected return type {@param clazz} (or {@code
+     * null} for no type check).
+     *
+     * This call should always be made after {@link #unparcel()} or inside a lock after making sure
+     * {@code mMap} is not null.
+     *
+     * @hide
+     */
+    final <T> T getValue(String key, @Nullable Class<T> clazz) {
         int i = mMap.indexOfKey(key);
-        return (i >= 0) ? getValueAt(i) : null;
+        return (i >= 0) ? getValueAt(i, clazz) : null;
     }
 
     /**
@@ -336,11 +348,12 @@
      *
      * @hide
      */
-    final Object getValueAt(int i) {
+    @SuppressWarnings("unchecked")
+    final <T> T getValueAt(int i, @Nullable Class<T> clazz) {
         Object object = mMap.valueAt(i);
-        if (object instanceof Supplier<?>) {
+        if (object instanceof Function<?, ?>) {
             try {
-                object = ((Supplier<?>) object).get();
+                object = ((Function<Class<?>, ?>) object).apply(clazz);
             } catch (BadParcelableException e) {
                 if (sShouldDefuse) {
                     Log.w(TAG, "Failed to parse item " + mMap.keyAt(i) + ", returning null.", e);
@@ -351,7 +364,7 @@
             }
             mMap.setValueAt(i, object);
         }
-        return object;
+        return (clazz != null) ? clazz.cast(object) : (T) object;
     }
 
     private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
@@ -528,7 +541,7 @@
         } else {
             // Following semantic above of failing in case we get a serialized value vs a
             // deserialized one, we'll compare the map. If a certain element hasn't been
-            // deserialized yet, it's a Supplier (or more specifically a LazyValue, but let's
+            // deserialized yet, it's a function object (or more specifically a LazyValue, but let's
             // pretend we don't know that here :P), we'll use that element's equality comparison as
             // map naturally does. That will takes care of comparing the payload if needed (see
             // Parcel.readLazyValue() for details).
@@ -982,15 +995,19 @@
     }
 
     // Log a message if the value was non-null but not of the expected type
-    void typeWarning(String key, Object value, String className,
-            Object defaultValue, ClassCastException e) {
+    void typeWarning(String key, @Nullable Object value, String className,
+            Object defaultValue, RuntimeException e) {
         StringBuilder sb = new StringBuilder();
         sb.append("Key ");
         sb.append(key);
         sb.append(" expected ");
         sb.append(className);
-        sb.append(" but value was a ");
-        sb.append(value.getClass().getName());
+        if (value != null) {
+            sb.append(" but value was a ");
+            sb.append(value.getClass().getName());
+        } else {
+            sb.append(" but value was of a different type ");
+        }
         sb.append(".  The default value ");
         sb.append(defaultValue);
         sb.append(" was returned.");
@@ -998,8 +1015,7 @@
         Log.w(TAG, "Attempt to cast generated internal exception:", e);
     }
 
-    void typeWarning(String key, Object value, String className,
-            ClassCastException e) {
+    void typeWarning(String key, @Nullable Object value, String className, RuntimeException e) {
         typeWarning(key, value, className, "<null>", e);
     }
 
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index b2bbfd6..2b13f20 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -16,6 +16,9 @@
 
 package android.os;
 
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.ArrayMap;
@@ -914,6 +917,33 @@
 
     /**
      * Returns the value associated with the given key, or {@code null} if
+     * no mapping of the desired type exists for the given key or a {@code null}
+     * value is explicitly associated with the key.
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * @param key a String, or {@code null}
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     * @return a Parcelable value, or {@code null}
+     *
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> T getParcelable(@Nullable String key, @NonNull Class<T> clazz) {
+        unparcel();
+        try {
+            return getValue(key, requireNonNull(clazz));
+        } catch (ClassCastException | BadParcelableException e) {
+            typeWarning(key, /* value */ null, "Parcelable", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if
      * no mapping of the desired type exists for the given key or a null
      * value is explicitly associated with the key.
      *
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index fcce266..39ca596 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -107,9 +107,7 @@
     void clearSeedAccountData(int userId);
     boolean someUserHasSeedAccount(in String accountName, in String accountType);
     boolean someUserHasAccount(in String accountName, in String accountType);
-    boolean isProfile(int userId);
-    boolean isManagedProfile(int userId);
-    boolean isCloneProfile(int userId);
+    String getProfileType(int userId);
     boolean isMediaSharedWithParent(int userId);
     boolean isCredentialSharedWithParent(int userId);
     boolean isDemoUser(int userId);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 9998e12..ae92353 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -4319,18 +4319,19 @@
     }
 
     /**
-     * This will return a {@link Supplier} for length-prefixed types that deserializes the object
-     * when {@link Supplier#get()} is called, for other types it will return the object itself.
+     * This will return a {@link Function} for length-prefixed types that deserializes the object
+     * when {@link Function#apply} is called with the expected class of the return object (or {@code
+     * null} for no type check), for other types it will return the object itself.
      *
-     * <p>After calling {@link Supplier#get()} the parcel cursor will not change. Note that you
-     * shouldn't recycle the parcel, not at least until all objects have been retrieved. No
+     * <p>After calling {@link Function#apply(Object)} the parcel cursor will not change. Note that
+     * you shouldn't recycle the parcel, not at least until all objects have been retrieved. No
      * synchronization attempts are made.
      *
-     * </p>The supplier returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
-     * suppliers are equal if either of the following is true:
+     * </p>The function returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
+     * function objects are equal if either of the following is true:
      * <ul>
-     *   <li>{@link Supplier#get()} has been called on both and both objects returned are equal.
-     *   <li>{@link Supplier#get()} hasn't been called on either one and everything below is true:
+     *   <li>{@link Function#apply} has been called on both and both objects returned are equal.
+     *   <li>{@link Function#apply} hasn't been called on either one and everything below is true:
      *   <ul>
      *       <li>The {@code loader} parameters used to retrieve each are equal.
      *       <li>They both have the same type.
@@ -4357,7 +4358,7 @@
     }
 
 
-    private static final class LazyValue implements Supplier<Object> {
+    private static final class LazyValue implements Function<Class<?>, Object> {
         /**
          *                      |   4B   |   4B   |
          * mSource = Parcel{... |  type  | length | object | ...}
@@ -4389,7 +4390,7 @@
         }
 
         @Override
-        public Object get() {
+        public Object apply(@Nullable Class<?> clazz) {
             Parcel source = mSource;
             if (source != null) {
                 synchronized (source) {
@@ -4398,7 +4399,7 @@
                         int restore = source.dataPosition();
                         try {
                             source.setDataPosition(mPosition);
-                            mObject = source.readValue(mLoader);
+                            mObject = source.readValue(mLoader, clazz);
                         } finally {
                             source.setDataPosition(restore);
                         }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c597a1a..e56f214 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -98,8 +98,8 @@
     /** The userId of the constructor param context. To be used instead of mContext.getUserId(). */
     private final @UserIdInt int mUserId;
 
-    private Boolean mIsManagedProfileCached;
-    private Boolean mIsProfileCached;
+    /** The userType of UserHandle.myUserId(); empty string if not a profile; null until cached. */
+    private String mProfileTypeOfProcessUser = null;
 
     /**
      * User type representing a {@link UserHandle#USER_SYSTEM system} user that is a human user.
@@ -2276,7 +2276,7 @@
      * {@link UserManager#USER_TYPE_PROFILE_MANAGED managed profile}.
      * @hide
      */
-    public static boolean isUserTypeManagedProfile(String userType) {
+    public static boolean isUserTypeManagedProfile(@Nullable String userType) {
         return USER_TYPE_PROFILE_MANAGED.equals(userType);
     }
 
@@ -2284,7 +2284,7 @@
      * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_GUEST guest user}.
      * @hide
      */
-    public static boolean isUserTypeGuest(String userType) {
+    public static boolean isUserTypeGuest(@Nullable String userType) {
         return USER_TYPE_FULL_GUEST.equals(userType);
     }
 
@@ -2293,7 +2293,7 @@
      * {@link UserManager#USER_TYPE_FULL_RESTRICTED restricted user}.
      * @hide
      */
-    public static boolean isUserTypeRestricted(String userType) {
+    public static boolean isUserTypeRestricted(@Nullable String userType) {
         return USER_TYPE_FULL_RESTRICTED.equals(userType);
     }
 
@@ -2301,7 +2301,7 @@
      * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_DEMO demo user}.
      * @hide
      */
-    public static boolean isUserTypeDemo(String userType) {
+    public static boolean isUserTypeDemo(@Nullable String userType) {
         return USER_TYPE_FULL_DEMO.equals(userType);
     }
 
@@ -2309,7 +2309,7 @@
      * Returns whether the user type is a {@link UserManager#USER_TYPE_PROFILE_CLONE clone user}.
      * @hide
      */
-    public static boolean isUserTypeCloneProfile(String userType) {
+    public static boolean isUserTypeCloneProfile(@Nullable String userType) {
         return USER_TYPE_PROFILE_CLONE.equals(userType);
     }
 
@@ -2525,25 +2525,50 @@
     }
 
     private boolean isProfile(@UserIdInt int userId) {
-        if (userId == mUserId) {
+        final String profileType = getProfileType(userId);
+        return profileType != null && !profileType.equals("");
+    }
+
+    /**
+     * Returns the user type of the context user if it is a profile.
+     *
+     * This is a more specific form of {@link #getUserType()} with relaxed permission requirements.
+     *
+     * @return the user type of the context user if it is a {@link #isProfile() profile},
+     *         an empty string if it is not a profile,
+     *         or null if the user doesn't exist.
+     */
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.QUERY_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
+    private @Nullable String getProfileType() {
+        return getProfileType(mUserId);
+    }
+
+    /** @see #getProfileType() */
+    private @Nullable String getProfileType(@UserIdInt int userId) {
+        // First, the typical case (i.e. the *process* user, not necessarily the context user).
+        // This cache cannot be become invalidated since it's about the calling process itself.
+        if (userId == UserHandle.myUserId()) {
             // No need for synchronization.  Once it becomes non-null, it'll be non-null forever.
             // Worst case we might end up calling the AIDL method multiple times but that's fine.
-            if (mIsProfileCached != null) {
-                return mIsProfileCached;
+            if (mProfileTypeOfProcessUser != null) {
+                return mProfileTypeOfProcessUser;
             }
             try {
-                mIsProfileCached = mService.isProfile(mUserId);
-                return mIsProfileCached;
-            } catch (RemoteException re) {
-                throw re.rethrowFromSystemServer();
-            }
-        } else {
-            try {
-                return mService.isProfile(userId);
+                final String profileType = mService.getProfileType(userId);
+                if (profileType != null) {
+                    return mProfileTypeOfProcessUser = profileType.intern();
+                }
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
         }
+
+        // The userId is not for the process's user. Use a slower cache that handles invalidation.
+        return mProfileTypeCache.query(userId);
     }
 
     /**
@@ -2577,50 +2602,26 @@
             android.Manifest.permission.QUERY_USERS,
             android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
     public boolean isManagedProfile(@UserIdInt int userId) {
-        if (userId == mUserId) {
-            // No need for synchronization.  Once it becomes non-null, it'll be non-null forever.
-            // Worst case we might end up calling the AIDL method multiple times but that's fine.
-            if (mIsManagedProfileCached != null) {
-                return mIsManagedProfileCached;
-            }
-            try {
-                mIsManagedProfileCached = mService.isManagedProfile(mUserId);
-                return mIsManagedProfileCached;
-            } catch (RemoteException re) {
-                throw re.rethrowFromSystemServer();
-            }
-        } else {
-            try {
-                return mService.isManagedProfile(userId);
-            } catch (RemoteException re) {
-                throw re.rethrowFromSystemServer();
-            }
-        }
+        return isUserTypeManagedProfile(getProfileType(userId));
     }
 
     /**
      * Checks if the context user is a clone profile.
      *
-     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
-     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
-     * must be in the same profile group of the user.
-     *
      * @return whether the context user is a clone profile.
      *
      * @see android.os.UserManager#USER_TYPE_PROFILE_CLONE
      * @hide
      */
     @SystemApi
-    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
-            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
-    @UserHandleAware
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.QUERY_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
     @SuppressAutoDoc
     public boolean isCloneProfile() {
-        try {
-            return mService.isCloneProfile(mUserId);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
+        return isUserTypeCloneProfile(getProfileType());
     }
 
     /**
@@ -4840,15 +4841,16 @@
      * @param overrideDevicePolicy when {@code true}, user is removed even if the caller has
      * the {@link #DISALLOW_REMOVE_USER} or {@link #DISALLOW_REMOVE_MANAGED_PROFILE} restriction
      *
-     * @return the result code {@link #REMOVE_RESULT_REMOVED}, {@link #REMOVE_RESULT_DEFERRED},
-     * {@link #REMOVE_RESULT_ALREADY_BEING_REMOVED}, or {@link #REMOVE_RESULT_ERROR}.
+     * @return the {@link RemoveResult} code: {@link #REMOVE_RESULT_REMOVED},
+     * {@link #REMOVE_RESULT_DEFERRED}, {@link #REMOVE_RESULT_ALREADY_BEING_REMOVED}, or
+     * {@link #REMOVE_RESULT_ERROR}.
      *
      * @hide
      */
     @SystemApi
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
-    public int removeUserWhenPossible(@NonNull UserHandle user,
+    public @RemoveResult int removeUserWhenPossible(@NonNull UserHandle user,
             boolean overrideDevicePolicy) {
         try {
             return mService.removeUserWhenPossible(user.getIdentifier(), overrideDevicePolicy);
@@ -5247,6 +5249,33 @@
         }
     }
 
+    /* Cache key for anything that assumes that userIds cannot be re-used without rebooting. */
+    private static final String CACHE_KEY_STATIC_USER_PROPERTIES = "cache_key.static_user_props";
+
+    private final PropertyInvalidatedCache<Integer, String> mProfileTypeCache =
+            new PropertyInvalidatedCache<Integer, String>(32, CACHE_KEY_STATIC_USER_PROPERTIES) {
+                @Override
+                public String recompute(Integer query) {
+                    try {
+                        // Will be null (and not cached) if invalid user; otherwise cache the type.
+                        String profileType = mService.getProfileType(query);
+                        if (profileType != null) profileType = profileType.intern();
+                        return profileType;
+                    } catch (RemoteException re) {
+                        throw re.rethrowFromSystemServer();
+                    }
+                }
+                @Override
+                public boolean bypass(Integer query) {
+                    return query < 0;
+                }
+            };
+
+    /** {@hide} */
+    public static final void invalidateStaticUserProperties() {
+        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STATIC_USER_PROPERTIES);
+    }
+
     /**
      * @hide
      * User that enforces a restriction.
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 0eb21e1..722cdbc 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -203,4 +203,6 @@
     void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 93;
     int getExternalStorageMountMode(int uid, in String packageName) = 94;
     boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 95;
+    void setCloudMediaProvider(in String authority) = 96;
+    String getCloudMediaProvider() = 97;
 }
\ No newline at end of file
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4d65f39..4e1337f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -3002,6 +3002,35 @@
         }
     }
 
+    /**
+     * Notify the system of the current cloud media provider.
+     *
+     * This can only be called by the {@link android.service.storage.ExternalStorageService}
+     * holding the {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission.
+     *
+     * @param authority the authority of the content provider
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void setCloudMediaProvider(@Nullable String authority) {
+        try {
+            mStorageManager.setCloudMediaProvider(authority);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    @TestApi
+    @Nullable
+    public String getCloudMediaProvider() {
+        try {
+            return mStorageManager.getCloudMediaProvider();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private final Object mFuseAppLoopLock = new Object();
 
     @GuardedBy("mFuseAppLoopLock")
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 8928a42..059bd84 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -114,7 +114,6 @@
      */
     public abstract void prepareAppDataAfterInstall(@NonNull String packageName, int uid);
 
-
     /**
      * Return true if uid is external storage service.
      */
@@ -151,4 +150,23 @@
      * it's ok to access and modify CE directories on volumes for this user.
      */
     public abstract boolean isCeStoragePrepared(@UserIdInt int userId);
+
+    /**
+     * A listener for changes to the cloud provider.
+     */
+    public interface CloudProviderChangeListener {
+        /**
+         * Triggered when the cloud provider changes. A {@code null} value means there's currently
+         * no cloud provider.
+         */
+        void onCloudProviderChanged(int userId, @Nullable String authority);
+    }
+
+    /**
+     * Register a {@link CloudProviderChangeListener} to be notified when a cloud media provider
+     * changes. The listener will be called after registration with any currently set cloud media
+     * providers.
+     */
+    public abstract void registerCloudProviderChangeListener(
+            @NonNull CloudProviderChangeListener listener);
 }
diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
index c806409..b20dccc 100644
--- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java
+++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
@@ -50,6 +50,13 @@
     public abstract boolean hasActiveSession(String packageName);
 
     /**
+     * Returns the package name of the active session.
+     *
+     * @param callingVoiceInteractor the voice interactor binder from the calling VoiceInteractor.
+     */
+    public abstract String getVoiceInteractorPackageName(IBinder callingVoiceInteractor);
+
+    /**
      * Gets the identity of the currently active HotwordDetectionService.
      *
      * @see HotwordDetectionServiceIdentity
@@ -82,4 +89,4 @@
             return mOwnerUid;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/speech/RecognitionSupport.java b/core/java/android/speech/RecognitionSupport.java
index 3a86d0b..43c8e1b 100644
--- a/core/java/android/speech/RecognitionSupport.java
+++ b/core/java/android/speech/RecognitionSupport.java
@@ -36,13 +36,16 @@
 
     /** Support for this request is ready for use on this device for the returned languages. */
     @NonNull
+    @DataClass.PluralOf("installedLanguage")
     private List<String> mInstalledLanguages = null;
 
     /** Support for this request is scheduled for download for the returned languages. */
+    @DataClass.PluralOf("pendingLanguage")
     @NonNull private List<String> mPendingLanguages = null;
 
     /** These languages are supported but need to be downloaded before use. */
     @NonNull
+    @DataClass.PluralOf("supportedLanguage")
     private List<String> mSupportedLanguages = null;
 
 
@@ -231,10 +234,7 @@
 
         /** @see #setInstalledLanguages */
         @DataClass.Generated.Member
-        public @NonNull Builder addInstalledLanguages(@NonNull String value) {
-            // You can refine this method's name by providing item's singular name, e.g.:
-            // @DataClass.PluralOf("item")) mItems = ...
-
+        public @NonNull Builder addInstalledLanguage(@NonNull String value) {
             if (mInstalledLanguages == null) setInstalledLanguages(new java.util.ArrayList<>());
             mInstalledLanguages.add(value);
             return this;
@@ -253,10 +253,7 @@
 
         /** @see #setPendingLanguages */
         @DataClass.Generated.Member
-        public @NonNull Builder addPendingLanguages(@NonNull String value) {
-            // You can refine this method's name by providing item's singular name, e.g.:
-            // @DataClass.PluralOf("item")) mItems = ...
-
+        public @NonNull Builder addPendingLanguage(@NonNull String value) {
             if (mPendingLanguages == null) setPendingLanguages(new java.util.ArrayList<>());
             mPendingLanguages.add(value);
             return this;
@@ -275,10 +272,7 @@
 
         /** @see #setSupportedLanguages */
         @DataClass.Generated.Member
-        public @NonNull Builder addSupportedLanguages(@NonNull String value) {
-            // You can refine this method's name by providing item's singular name, e.g.:
-            // @DataClass.PluralOf("item")) mItems = ...
-
+        public @NonNull Builder addSupportedLanguage(@NonNull String value) {
             if (mSupportedLanguages == null) setSupportedLanguages(new java.util.ArrayList<>());
             mSupportedLanguages.add(value);
             return this;
@@ -314,10 +308,10 @@
     }
 
     @DataClass.Generated(
-            time = 1639158640137L,
+            time = 1644582623366L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/speech/RecognitionSupport.java",
-            inputSignatures = "private @android.annotation.NonNull java.util.List<java.lang.String> mInstalledLanguages\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mPendingLanguages\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mSupportedLanguages\nclass RecognitionSupport extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+            inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"installedLanguage\") java.util.List<java.lang.String> mInstalledLanguages\nprivate @com.android.internal.util.DataClass.PluralOf(\"pendingLanguage\") @android.annotation.NonNull java.util.List<java.lang.String> mPendingLanguages\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"supportedLanguage\") java.util.List<java.lang.String> mSupportedLanguages\nclass RecognitionSupport extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 43df294..e313388 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -11,6 +11,9 @@
 roosa@google.com
 jreck@google.com
 
+# Autofill
+per-file ViewStructure.java = file:/core/java/android/service/autofill/OWNERS
+
 # Display
 per-file Display*.java = file:/services/core/java/com/android/server/display/OWNERS
 per-file Display*.aidl = file:/services/core/java/com/android/server/display/OWNERS
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a2fcf80..1a458ce 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -23,7 +23,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.CompatibilityInfo.Translator;
 import android.graphics.BLASTBufferQueue;
@@ -42,7 +41,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.SystemClock;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.SurfaceControl.Transaction;
@@ -135,7 +133,7 @@
     private boolean mDisableBackgroundLayer = false;
 
     /**
-     * We use this lock to protect access to mSurfaceControl and 
+     * We use this lock to protect access to mSurfaceControl and
      * SurfaceViewPositionUpdateListener#mPositionChangedTransaction. Both are accessed on the UI
      * thread and the render thread.
      */
@@ -376,11 +374,7 @@
     }
 
     private void updateSurfaceAlpha() {
-        if (!mUseAlpha) {
-            if (DEBUG) {
-                Log.d(TAG, System.identityHashCode(this)
-                        + " updateSurfaceAlpha: setUseAlpha() is not called, ignored.");
-            }
+        if (!mUseAlpha || !mHaveFrame || mSurfaceControl == null) {
             return;
         }
         final float viewAlpha = getAlpha();
@@ -389,88 +383,16 @@
                     + " updateSurfaceAlpha:"
                     + " translucent color is not supported for a surface placed z-below.");
         }
-        if (!mHaveFrame) {
-            if (DEBUG) {
-                Log.d(TAG, System.identityHashCode(this)
-                        + " updateSurfaceAlpha: has no surface.");
-            }
-            return;
-        }
         final ViewRootImpl viewRoot = getViewRootImpl();
         if (viewRoot == null) {
-            if (DEBUG) {
-                Log.d(TAG, System.identityHashCode(this)
-                        + " updateSurfaceAlpha: ViewRootImpl not available.");
-            }
-            return;
-        }
-        if (mSurfaceControl == null) {
-            if (DEBUG) {
-                Log.d(TAG, System.identityHashCode(this)
-                        + "updateSurfaceAlpha:"
-                        + " surface is not yet created, or already released.");
-            }
-            return;
-        }
-        final Surface parent = viewRoot.mSurface;
-        if (parent == null || !parent.isValid()) {
-            if (DEBUG) {
-                Log.d(TAG, System.identityHashCode(this)
-                        + " updateSurfaceAlpha: ViewRootImpl has no valid surface");
-            }
             return;
         }
         final float alpha = getFixedAlpha();
         if (alpha != mSurfaceAlpha) {
-            if (isHardwareAccelerated()) {
-                /*
-                 * Schedule a callback that reflects an alpha value onto the underlying surfaces.
-                 * This gets called on a RenderThread worker thread, so members accessed here must
-                 * be protected by a lock.
-                 */
-                viewRoot.registerRtFrameCallback(frame -> {
-                    try {
-                        synchronized (mSurfaceControlLock) {
-                            if (!parent.isValid()) {
-                                if (DEBUG) {
-                                    Log.d(TAG, System.identityHashCode(this)
-                                            + " updateSurfaceAlpha RT:"
-                                            + " ViewRootImpl has no valid surface");
-                                }
-                                return;
-                            }
-                            if (mSurfaceControl == null) {
-                                if (DEBUG) {
-                                    Log.d(TAG, System.identityHashCode(this)
-                                            + "updateSurfaceAlpha RT:"
-                                            + " mSurfaceControl has already released");
-                                }
-                                return;
-                            }
-                            if (DEBUG) {
-                                Log.d(TAG, System.identityHashCode(this)
-                                        + " updateSurfaceAlpha RT: set alpha=" + alpha);
-                            }
-
-                            mFrameCallbackTransaction.setAlpha(mSurfaceControl, alpha);
-                            applyOrMergeTransaction(mFrameCallbackTransaction, frame);
-                        }
-                        // It's possible that mSurfaceControl is released in the UI thread before
-                        // the transaction completes. If that happens, an exception is thrown, which
-                        // must be caught immediately.
-                    } catch (Exception e) {
-                        Log.e(TAG, System.identityHashCode(this)
-                                + "updateSurfaceAlpha RT: Exception during surface transaction", e);
-                    }
-                });
-                damageInParent();
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, System.identityHashCode(this)
-                            + " updateSurfaceAlpha: set alpha=" + alpha);
-                }
-                mTmpTransaction.setAlpha(mSurfaceControl, alpha).apply();
-            }
+            final Transaction transaction = new Transaction();
+            transaction.setAlpha(mSurfaceControl, alpha);
+            viewRoot.applyTransactionOnDraw(transaction);
+            damageInParent();
             mSurfaceAlpha = alpha;
         }
     }
@@ -527,12 +449,7 @@
         mRequestedVisible = false;
 
         updateSurface();
-
-        // We don't release this as part of releaseSurfaces as
-        // that is also called on transient visibility changes. We can't
-        // recreate this Surface, so only release it when we are fully
-        // detached.
-        tryReleaseSurfaces(true /* releaseSurfacePackage*/);
+        releaseSurfaces(true /* releaseSurfacePackage*/);
 
         mHaveFrame = false;
         super.onDetachedFromWindow();
@@ -625,7 +542,7 @@
     public void setClipBounds(Rect clipBounds) {
         super.setClipBounds(clipBounds);
 
-        if (!mClipSurfaceToBounds) {
+        if (!mClipSurfaceToBounds || mSurfaceControl == null) {
             return;
         }
 
@@ -635,18 +552,15 @@
             invalidate();
         }
 
-        if (mSurfaceControl != null) {
-            if (mClipBounds != null) {
-                mTmpRect.set(mClipBounds);
-            } else {
-                mTmpRect.set(0, 0, mSurfaceWidth, mSurfaceHeight);
-            }
-            SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(this);
-            applier.scheduleApply(
-                    new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(mSurfaceControl)
-                            .withWindowCrop(mTmpRect)
-                            .build());
+        if (mClipBounds != null) {
+            mTmpRect.set(mClipBounds);
+        } else {
+            mTmpRect.set(0, 0, mSurfaceWidth, mSurfaceHeight);
         }
+        final Transaction transaction = new Transaction();
+        transaction.setWindowCrop(mSurfaceControl, mTmpRect);
+        applyTransactionOnVriDraw(transaction);
+        invalidate();
     }
 
     private void clearSurfaceViewPort(Canvas canvas) {
@@ -782,37 +696,10 @@
         if (viewRoot == null) {
             return true;
         }
-        final Surface parent = viewRoot.mSurface;
-        if (parent == null || !parent.isValid()) {
-            return true;
-        }
-
-        /*
-         * Schedule a callback that reflects an alpha value onto the underlying surfaces.
-         * This gets called on a RenderThread worker thread, so members accessed here must
-         * be protected by a lock.
-         */
-        viewRoot.registerRtFrameCallback(frame -> {
-            try {
-                synchronized (mSurfaceControlLock) {
-                    if (!parent.isValid() || mSurfaceControl == null) {
-                        return;
-                    }
-
-                    updateRelativeZ(mFrameCallbackTransaction);
-                    applyOrMergeTransaction(mFrameCallbackTransaction, frame);
-                }
-                // It's possible that mSurfaceControl is released in the UI thread before
-                // the transaction completes. If that happens, an exception is thrown, which
-                // must be caught immediately.
-             } catch (Exception e) {
-                Log.e(TAG, System.identityHashCode(this)
-                        + "setZOrderOnTop RT: Exception during surface transaction", e);
-            }
-        });
-
+        final Transaction transaction = new SurfaceControl.Transaction();
+        updateRelativeZ(transaction);
+        viewRoot.applyTransactionOnDraw(transaction);
         invalidate();
-
         return true;
     }
 
@@ -863,7 +750,7 @@
         return t;
     }
 
-    private void tryReleaseSurfaces(boolean releaseSurfacePackage) {
+    private void releaseSurfaces(boolean releaseSurfacePackage) {
         mSurfaceAlpha = 1f;
 
         synchronized (mSurfaceControlLock) {
@@ -892,12 +779,7 @@
                 mSurfacePackage = null;
             }
 
-            ViewRootImpl viewRoot = getViewRootImpl();
-            if (viewRoot != null) {
-                viewRoot.applyTransactionOnDraw(transaction);
-            } else {
-                transaction.apply();
-            }
+            applyTransactionOnVriDraw(transaction);
         }
     }
 
@@ -1041,7 +923,7 @@
 
         if (viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
             notifySurfaceDestroyed();
-            tryReleaseSurfaces(false /* releaseSurfacePackage*/);
+            releaseSurfaces(false /* releaseSurfacePackage*/);
             return;
         }
 
@@ -1182,7 +1064,7 @@
                 } finally {
                     mIsCreating = false;
                     if (mSurfaceControl != null && !mSurfaceCreated) {
-                        tryReleaseSurfaces(false /* releaseSurfacePackage*/);
+                        releaseSurfaces(false /* releaseSurfacePackage*/);
                     }
                 }
             } catch (Exception ex) {
@@ -1317,17 +1199,6 @@
     }
 
     /**
-     * A place to over-ride for applying child-surface transactions.
-     * These can be synchronized with the viewroot surface using deferTransaction.
-     *
-     * Called from RenderWorker while UI thread is paused.
-     * @hide
-     */
-    protected void applyChildSurfaceTransaction_renderWorker(SurfaceControl.Transaction t,
-            Surface viewRootSurface, long nextViewRootFrameNumber) {
-    }
-
-    /**
      * Sets the surface position and scale. Can be called on
      * the UI thread as well as on the renderer thread.
      *
@@ -1435,11 +1306,6 @@
                     if (mViewVisibility) {
                         mPositionChangedTransaction.show(mSurfaceControl);
                     }
-                    final ViewRootImpl viewRoot = getViewRootImpl();
-                    if (viewRoot != null) {
-                        applyChildSurfaceTransaction_renderWorker(mPositionChangedTransaction,
-                                viewRoot.mSurface, frameNumber);
-                    }
                     applyOrMergeTransaction(mPositionChangedTransaction, frameNumber);
                     mPendingTransaction = false;
                 } catch (Exception ex) {
@@ -1837,12 +1703,7 @@
                 mSurfacePackage.release();
             }
             reparentSurfacePackage(transaction, p);
-            final ViewRootImpl viewRoot = getViewRootImpl();
-            if (viewRoot != null) {
-                viewRoot.applyTransactionOnDraw(transaction);
-            } else {
-                transaction.apply();
-            }
+            applyTransactionOnVriDraw(transaction);
         }
         mSurfacePackage = p;
         invalidate();
@@ -1948,4 +1809,13 @@
         }
     }
 
+    private void applyTransactionOnVriDraw(Transaction t) {
+        final ViewRootImpl viewRoot = getViewRootImpl();
+        if (viewRoot != null) {
+            // If we are using BLAST, merge the transaction with the viewroot buffer transaction.
+            viewRoot.applyTransactionOnDraw(t);
+        } else {
+            t.apply();
+        }
+    }
 }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 150eb65..4ae6bf7 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -217,12 +217,6 @@
     private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
     public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
 
-    private static final String[] QUERY_FILE_INFO_PROJECTION = {
-        OpenableColumns.DISPLAY_NAME,
-        Downloads.Impl.COLUMN_TITLE,
-        DocumentsContract.Document.COLUMN_FLAGS
-    };
-
     private static final String PLURALS_COUNT = "count";
     private static final String PLURALS_FILE_NAME = "file_name";
 
@@ -1480,15 +1474,15 @@
      * and to avoid mocking Android core classes.
      */
     @VisibleForTesting
-    public Cursor queryResolver(ContentResolver resolver, String[] projection, Uri uri) {
-        return resolver.query(uri, projection, null, null, null);
+    public Cursor queryResolver(ContentResolver resolver, Uri uri) {
+        return resolver.query(uri, null, null, null, null);
     }
 
     private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) {
         String fileName = null;
         boolean hasThumbnail = false;
 
-        try (Cursor cursor = queryResolver(resolver, QUERY_FILE_INFO_PROJECTION, uri)) {
+        try (Cursor cursor = queryResolver(resolver, uri)) {
             if (cursor != null && cursor.getCount() > 0) {
                 int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                 int titleIndex = cursor.getColumnIndex(Downloads.Impl.COLUMN_TITLE);
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 7b6df6c..e58f4f0 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -22,7 +22,6 @@
 import android.view.View;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.FrameworkStatsLog;
 
 /**
  * Writes sysui_multi_event records to the system event log.
@@ -53,10 +52,7 @@
     }
 
     protected void saveLog(LogMaker log) {
-        // TODO(b/116684537): Flag guard logging to event log and statsd socket.
         EventLogTags.writeSysuiMultiAction(log.serialize());
-        FrameworkStatsLog.write(FrameworkStatsLog.KEY_VALUE_PAIRS_ATOM,
-                /* UID is retrieved from statsd side */ 0, log.getEntries());
     }
 
     public static final int VIEW_UNKNOWN = MetricsEvent.VIEW_UNKNOWN;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 955f46b..63b704c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -252,7 +252,6 @@
                 "libandroid_net",
                 "libandroidicu",
                 "libbattery",
-                "libbpf_android",
                 "libnetdutils",
                 "libmemtrack",
                 "libandroidfw",
diff --git a/core/jni/android_app_admin_SecurityLog.cpp b/core/jni/android_app_admin_SecurityLog.cpp
index e5a13db..d197edc 100644
--- a/core/jni/android_app_admin_SecurityLog.cpp
+++ b/core/jni/android_app_admin_SecurityLog.cpp
@@ -85,10 +85,6 @@
       (void*) android_app_admin_SecurityLog_isLoggingEnabled
     },
     { "writeEvent",
-      "(ILjava/lang/String;)I",
-      (void*) SLog::writeEventString
-    },
-    { "writeEvent",
       "(I[Ljava/lang/Object;)I",
       (void*) SLog::writeEventArray
     },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0c41f31..68c8143 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5917,10 +5917,9 @@
 
     <!-- @SystemApi Allows an application to manage the wallpaper effects
      generation service.
-        @hide  <p>Not for use by third-party applications.</p> -->
+     @hide  <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION"
-        android:protectionLevel="signature" />
-
+        android:protectionLevel="signature|role" />
 
     <!-- Allows an app to set the theme overlay in /vendor/overlay
          being used.
@@ -6256,7 +6255,10 @@
                 android:protectionLevel="signature|privileged" />
 
     <!-- Allows an application to read nearby streaming policy. The policy controls
-         whether to allow the device to stream its notifications and apps to nearby devices. -->
+         whether to allow the device to stream its notifications and apps to nearby devices.
+         Applications that are not the device owner will need this permission to call
+         {@link android.app.admin.DevicePolicyManager#getNearbyNotificationStreamingPolicy} or
+         {@link android.app.admin.DevicePolicyManager#getNearbyAppStreamingPolicy}. -->
     <permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY"
         android:protectionLevel="normal" />
 
@@ -6378,6 +6380,11 @@
     <permission android:name="android.permission.TIS_EXTENSION_INTERFACE"
         android:protectionLevel="signature|privileged|vendorPrivileged" />
 
+    <!-- @SystemApi Allows an application to write to the security log buffer in logd.
+         @hide -->
+    <permission android:name="android.permission.WRITE_SECURITY_LOG"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/core/res/res/drawable/toast_frame.xml b/core/res/res/drawable/toast_frame.xml
index 44c00c0..a8cdef6 100644
--- a/core/res/res/drawable/toast_frame.xml
+++ b/core/res/res/drawable/toast_frame.xml
@@ -17,7 +17,7 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="?android:attr/colorBackground" />
+    <solid android:color="?android:attr/colorSurface" />
     <corners android:radius="28dp" />
 </shape>
 
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3a9cb2e..fca2bd1 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3989,31 +3989,34 @@
         <attr name="isAccessibilityTool" format="boolean" />
 
         <!-- Animated image of the accessibility service purpose or behavior, to help users
-             understand how the service can help them.-->
+             understand how the service can help them. -->
         <attr name="animatedImageDrawable" format="reference"/>
-        <!-- Html description of the accessibility service, to help users understand
-             how the service can help them.-->
+        <!-- Html description of the accessibility service usage, availability, or limitations (e.g.
+             isn't supported by all apps). -->
         <attr name="htmlDescription" format="reference"/>
-
-        <!-- Short description of the accessibility service purpose or behavior.-->
+        <!-- Description of the accessibility service usage, availability, or limitations (e.g.
+             isn't supported by all apps). -->
         <attr name="description" />
         <!-- Brief summary of the accessibility service purpose or behavior. -->
         <attr name="summary" />
+        <!-- Detailed intro of the accessibility service purpose or behavior. -->
+        <attr name="intro" format="reference" />
     </declare-styleable>
 
     <!-- Use <code>accessibility-shortcut-target</code> as the root tag of the XML resource that
          describes an activity, which is referenced from the
          <code>android.accessibilityshortcut.target</code> meta-data entry. -->
     <declare-styleable name="AccessibilityShortcutTarget">
-        <!-- Short description of the target of accessibility shortcut purpose or behavior.-->
+        <!-- Description of the target of accessibility shortcut usage, availability, or limitations
+             (e.g. isn't supported by all apps). -->
         <attr name="description" />
         <!-- Brief summary of the target of accessibility shortcut purpose or behavior. -->
         <attr name="summary" />
         <!-- Animated image of the target of accessibility shortcut purpose or behavior, to help
              users understand how the target of accessibility shortcut can help them.-->
         <attr name="animatedImageDrawable" format="reference"/>
-        <!-- Html description of the target of accessibility shortcut purpose or behavior, to help
-             users understand how the target of accessibility shortcut can help them. -->
+        <!-- Html description of the target of accessibility shortcut usage, availability, or
+             limitations (e.g. isn't supported by all apps). -->
         <attr name="htmlDescription" format="reference"/>
         <!-- Component name of an activity that allows the user to modify the settings for this
              target of accessibility shortcut. -->
@@ -4023,6 +4026,8 @@
              settings to remind users this accessibility service has a
              {@link android.service.quicksettings.TileService}. -->
         <attr name="tileService" format="string" />
+        <!-- Detailed intro of the target of accessibility shortcut purpose or behavior. -->
+        <attr name="intro" format="reference" />
     </declare-styleable>
 
     <!-- Use <code>print-service</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6eb19e1..5e6d05a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1307,6 +1307,7 @@
     <!-- Control the behavior when the user double-taps the home button.
             0 - Nothing
             1 - Recent apps view in SystemUI
+            2 - Picture-in-picture menu
          This needs to match the constants in
          policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
     -->
@@ -5771,4 +5772,21 @@
          current drain threshold.
     -->
     <integer name="config_bg_current_drain_location_min_duration">1800</integer>
+
+    <!-- The behavior for an app with a FGS and its notification is still showing, when the system
+         detects it's abusive and should be put into bg restricted level. True - we'll
+         show the prompt to user, False - we'll not show it.
+    -->
+    <bool name="config_bg_prompt_fgs_with_noti_to_bg_restricted">false</bool>
+
+    <!-- The types of state where we'll exempt its battery usage during that state.
+         The state here must be one or a combination of STATE_TYPE_* in BaseAppStateTracker.
+    -->
+    <integer name="config_bg_current_drain_exempted_types">9</integer>
+
+    <!-- The behavior when an app has the permission ACCESS_BACKGROUND_LOCATION granted,
+         whether or not the system will use a higher threshold towards its background battery usage
+         because of it.
+    -->
+    <bool name="config_bg_current_drain_high_threshold_by_bg_location">false</bool>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3d80ca8..7bf34a1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3278,6 +3278,7 @@
     <public name="windowSplashScreenBehavior" />
     <public name="allowUntrustedActivityEmbedding" />
     <public name="knownActivityEmbeddingCerts" />
+    <public name="intro" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c63e543..ff1c70a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3699,6 +3699,9 @@
   <java-symbol type="string" name="config_defaultSystemCaptionsService" />
   <java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
   <java-symbol type="string" name="config_defaultAmbientContextDetectionService" />
+  <java-symbol type="string" name="config_defaultAmbientContextConsentComponent" />
+  <java-symbol type="string" name="config_ambientContextPackageNameExtraKey" />
+  <java-symbol type="string" name="config_ambientContextEventArrayExtraKey" />
   <java-symbol type="string" name="config_retailDemoPackage" />
   <java-symbol type="string" name="config_retailDemoPackageSignature" />
 
@@ -4747,4 +4750,7 @@
   <java-symbol type="array" name="config_bg_current_drain_high_threshold_to_bg_restricted" />
   <java-symbol type="integer" name="config_bg_current_drain_media_playback_min_duration" />
   <java-symbol type="integer" name="config_bg_current_drain_location_min_duration" />
+  <java-symbol type="bool" name="config_bg_prompt_fgs_with_noti_to_bg_restricted" />
+  <java-symbol type="integer" name="config_bg_current_drain_exempted_types" />
+  <java-symbol type="bool" name="config_bg_current_drain_high_threshold_by_bg_location" />
 </resources>
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index 21613a8..e51eab6 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -143,6 +143,9 @@
     <!-- ResourcesLocaleResolutionTest -->
     <string name="dummy_string">dummy string</string>
 
+    <!-- Intro of the accessibility shortcut [CHAR LIMIT=NONE] -->
+    <string name="accessibility_shortcut_intro">Accessibility shortcut intro</string>
+
     <!-- Description of the accessibility shortcut [CHAR LIMIT=NONE] -->
     <string name="accessibility_shortcut_description">Accessibility shortcut description</string>
 
diff --git a/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
index 5fc536a..52fe65d 100644
--- a/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
+++ b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
@@ -23,4 +23,4 @@
     android:htmlDescription="@string/accessibility_shortcut_html_description"
     android:settingsActivity="com.example.shortcut.target.SettingsActivity"
     android:tileService="com.example.shortcut.target.TileService"
-/>
\ No newline at end of file
+    android:intro="@string/accessibility_shortcut_intro" />
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
index 76fb520..f605a00 100644
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
@@ -89,6 +89,16 @@
     }
 
     @Test
+    public void testIntro() {
+        final String intro = mTargetContext.getResources()
+                .getString(R.string.accessibility_shortcut_intro);
+
+        assertNotNull("Can't find intro string", intro);
+        assertThat("Intro is not correct",
+                mShortcutInfo.loadIntro(mPackageManager), is(intro));
+    }
+
+    @Test
     public void testAnimatedImageRes() {
         assertThat("Animated image resource id is not correct",
                 mShortcutInfo.getAnimatedImageRes(), is(R.drawable.bitmap_drawable));
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 139bc36..7f85982 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -192,7 +192,7 @@
     }
 
     @Override
-    public Cursor queryResolver(ContentResolver resolver, String[] projection, Uri uri) {
+    public Cursor queryResolver(ContentResolver resolver, Uri uri) {
         if (sOverrides.resolverCursor != null) {
             return sOverrides.resolverCursor;
         }
@@ -201,7 +201,7 @@
             throw new SecurityException("Test exception handling");
         }
 
-        return super.queryResolver(resolver, projection, uri);
+        return super.queryResolver(resolver, uri);
     }
 
     @Override
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 6ed1ba9..6ffcf10 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
@@ -618,7 +618,8 @@
     }
 
     /** Contains information to help position things on the screen. */
-    BubblePositioner getPositioner() {
+    @VisibleForTesting
+    public BubblePositioner getPositioner() {
         return mBubblePositioner;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 9ae67a9..da8308e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -60,6 +60,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.wm.shell.R;
 import com.android.wm.shell.TaskView;
@@ -418,8 +419,9 @@
         mPointerView.setBackground(mCurrentPointer);
     }
 
-    private String getBubbleKey() {
-        return mBubble != null ? mBubble.getKey() : "null";
+    @VisibleForTesting
+    public String getBubbleKey() {
+        return mBubble != null ? mBubble.getKey() : mIsOverflow ? BubbleOverflow.KEY : null;
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index dd751d2..eb7929b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -54,6 +54,7 @@
 
     /** Call before use and again if cleanUpExpandedState was called.  */
     fun initialize(controller: BubbleController) {
+        createExpandedView()
         getExpandedView()?.initialize(controller, controller.stackView, true /* isOverflow */)
     }
 
@@ -123,13 +124,15 @@
         overflowBtn?.updateDotVisibility(true /* animate */)
     }
 
+    fun createExpandedView(): BubbleExpandedView? {
+        expandedView = inflater.inflate(R.layout.bubble_expanded_view,
+                null /* root */, false /* attachToRoot */) as BubbleExpandedView
+        expandedView?.applyThemeAttrs()
+        updateResources()
+        return expandedView
+    }
+
     override fun getExpandedView(): BubbleExpandedView? {
-        if (expandedView == null) {
-            expandedView = inflater.inflate(R.layout.bubble_expanded_view,
-                    null /* root */, false /* attachToRoot */) as BubbleExpandedView
-            expandedView?.applyThemeAttrs()
-            updateResources()
-        }
         return expandedView
     }
 
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 c2eb08c..9219baa 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
@@ -787,7 +787,7 @@
         ta.recycle();
 
         final Runnable onBubbleAnimatedOut = () -> {
-            if (getBubbleCount() == 0 && !mBubbleData.isShowingOverflow()) {
+            if (getBubbleCount() == 0) {
                 mBubbleController.onAllBubblesAnimatedOut();
             }
         };
@@ -1651,7 +1651,6 @@
                 } else {
                     bubble.cleanupViews();
                 }
-                updatePointerPosition(false /* forIme */);
                 updateExpandedView();
                 logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
                 return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 34d98ee..5af1530 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -419,8 +419,8 @@
                     // hasRoundedCorners is currently only enabled for tasks
                     final Context displayContext =
                             mDisplayController.getDisplayContext(change.getTaskInfo().displayId);
-                    cornerRadius =
-                            ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
+                    cornerRadius = displayContext == null ? 0
+                            : ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
                 } else {
                     cornerRadius = 0;
                 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 7a1d619..1e30f6b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -47,6 +47,7 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group4
+@FlakyTest(bugId = 218604389)
 open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
     private val imeApp = ImeAppHelper(instrumentation)
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 27873bb..c8ced1c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -64,6 +64,7 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group4
+@FlakyTest(bugId = 218604389)
 open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
     private val fixedApp = FixedAppHelper(instrumentation)
     private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.startRotation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index d35654e..d65388b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -47,6 +47,7 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group4
+@FlakyTest(bugId = 218604389)
 open class SetRequestedOrientationWhilePinnedTest(
     testSpec: FlickerTestParameter
 ) : PipTransition(testSpec) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
new file mode 100644
index 0000000..8278c67
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.bubbles.BubbleOverflow;
+import com.android.wm.shell.bubbles.BubbleStackView;
+import com.android.wm.shell.bubbles.TestableBubblePositioner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link com.android.wm.shell.bubbles.BubbleOverflow}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class BubbleOverflowTest extends ShellTestCase {
+
+    private TestableBubblePositioner mPositioner;
+    private BubbleOverflow mOverflow;
+
+    @Mock
+    private BubbleController mBubbleController;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mPositioner = new TestableBubblePositioner(mContext, mock(WindowManager.class));
+        when(mBubbleController.getPositioner()).thenReturn(mPositioner);
+        when(mBubbleController.getStackView()).thenReturn(mock(BubbleStackView.class));
+
+        mOverflow = new BubbleOverflow(mContext, mPositioner);
+    }
+
+    @Test
+    public void test_initialize() {
+        assertThat(mOverflow.getExpandedView()).isNull();
+
+        mOverflow.initialize(mBubbleController);
+
+        assertThat(mOverflow.getExpandedView()).isNotNull();
+        assertThat(mOverflow.getExpandedView().getBubbleKey()).isEqualTo(BubbleOverflow.KEY);
+    }
+
+    @Test
+    public void test_cleanUpExpandedState() {
+        mOverflow.createExpandedView();
+        assertThat(mOverflow.getExpandedView()).isNotNull();
+
+        mOverflow.cleanUpExpandedState();
+        assertThat(mOverflow.getExpandedView()).isNull();
+    }
+
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8ba3539..a6fdf6c 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -402,6 +402,14 @@
 
     boolean isSpatializerAvailable();
 
+    boolean isSpatializerAvailableForDevice(in AudioDeviceAttributes device);
+
+    boolean hasHeadTracker(in AudioDeviceAttributes device);
+
+    void setHeadTrackerEnabled(boolean enabled, in AudioDeviceAttributes device);
+
+    boolean isHeadTrackerEnabled(in AudioDeviceAttributes device);
+
     void setSpatializerEnabled(boolean enabled);
 
     boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af);
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index 030d212..be0ef37 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -108,6 +108,83 @@
         }
     }
 
+    /**
+     * @hide
+     * Returns whether spatialization is available for a given audio device
+     * Reasons for spatialization being unavailable include situations where audio output is
+     * incompatible with sound spatialization, such as the device being a monophonic speaker, or
+     * the spatializer effect not supporting transaural processing when querying for speaker.
+     * @param device the audio device for which spatializer availability is queried
+     * @return {@code true} if the spatializer effect is available and capable
+     *         of processing the audio over the given audio device,
+     *         {@code false} otherwise.
+     * @see #isEnabled()
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    public boolean isAvailableForDevice(@NonNull AudioDeviceAttributes device)  {
+        Objects.requireNonNull(device);
+        try {
+            return mAm.getService().isSpatializerAvailableForDevice(device);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     * Returns whether the given device has an associated headtracker
+     * @param device the audio device to query
+     * @return true if the device has a head tracker, false otherwise
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    public boolean hasHeadTracker(@NonNull AudioDeviceAttributes device) {
+        Objects.requireNonNull(device);
+        try {
+            return mAm.getService().hasHeadTracker(device);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     * Enables or disables the head tracker of the given device
+     * @param enabled true to enable, false to disable
+     * @param device the device whose head tracker state is changed
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    public void setHeadTrackerEnabled(boolean enabled, @NonNull AudioDeviceAttributes device) {
+        Objects.requireNonNull(device);
+        try {
+            mAm.getService().setHeadTrackerEnabled(enabled, device);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Returns whether the head tracker of the device is enabled
+     * @param device the device to query
+     * @return true if the head tracker is enabled, false if disabled or if there isn't one
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    public boolean isHeadTrackerEnabled(@NonNull AudioDeviceAttributes device) {
+        Objects.requireNonNull(device);
+        try {
+            return mAm.getService().isHeadTrackerEnabled(device);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
     /** @hide */
     @IntDef(flag = false, value = {
             SPATIALIZER_IMMERSIVE_LEVEL_OTHER,
diff --git a/media/java/android/media/metrics/BundleSession.java b/media/java/android/media/metrics/BundleSession.java
new file mode 100644
index 0000000..743d233
--- /dev/null
+++ b/media/java/android/media/metrics/BundleSession.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 android.media.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.Objects;
+
+/**
+ * An instances of this class represents a session with data stored in a bundle.
+ */
+public final class BundleSession implements AutoCloseable {
+    private final @NonNull String mId;
+    private final @NonNull MediaMetricsManager mManager;
+    private final @NonNull LogSessionId mLogSessionId;
+
+    /** @hide */
+    public BundleSession(@NonNull String id, @NonNull MediaMetricsManager manager) {
+        mId = id;
+        mManager = manager;
+        AnnotationValidations.validate(NonNull.class, null, mId);
+        AnnotationValidations.validate(NonNull.class, null, mManager);
+        mLogSessionId = new LogSessionId(mId);
+    }
+
+    /**
+     * Reports metrics via bundle.
+     *
+     */
+    public void reportBundleMetrics(@NonNull PersistableBundle metrics) {
+        mManager.reportBundleMetrics(mId, metrics);
+    }
+
+    public @NonNull LogSessionId getSessionId() {
+        return mLogSessionId;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        BundleSession that = (BundleSession) o;
+        return Objects.equals(mId, that.mId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mId);
+    }
+
+    @Override
+    public void close() {
+    }
+}
diff --git a/media/java/android/media/metrics/EditingSession.java b/media/java/android/media/metrics/EditingSession.java
new file mode 100644
index 0000000..2a48a72
--- /dev/null
+++ b/media/java/android/media/metrics/EditingSession.java
@@ -0,0 +1,63 @@
+/*
+ * 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.media.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.Objects;
+
+/**
+ * An instances of this class represents a session of media editing.
+ */
+public final class EditingSession implements AutoCloseable {
+    private final @NonNull String mId;
+    private final @NonNull MediaMetricsManager mManager;
+    private final @NonNull LogSessionId mLogSessionId;
+
+    /** @hide */
+    public EditingSession(@NonNull String id, @NonNull MediaMetricsManager manager) {
+        mId = id;
+        mManager = manager;
+        AnnotationValidations.validate(NonNull.class, null, mId);
+        AnnotationValidations.validate(NonNull.class, null, mManager);
+        mLogSessionId = new LogSessionId(mId);
+    }
+
+    public @NonNull LogSessionId getSessionId() {
+        return mLogSessionId;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        EditingSession that = (EditingSession) o;
+        return Objects.equals(mId, that.mId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mId);
+    }
+
+    @Override
+    public void close() {
+    }
+}
diff --git a/media/java/android/media/metrics/IMediaMetricsManager.aidl b/media/java/android/media/metrics/IMediaMetricsManager.aidl
index f2c0d44..a774403 100644
--- a/media/java/android/media/metrics/IMediaMetricsManager.aidl
+++ b/media/java/android/media/metrics/IMediaMetricsManager.aidl
@@ -21,6 +21,7 @@
 import android.media.metrics.PlaybackMetrics;
 import android.media.metrics.PlaybackStateEvent;
 import android.media.metrics.TrackChangeEvent;
+import android.os.PersistableBundle;
 
 /**
  * Interface to the playback manager service.
@@ -28,10 +29,16 @@
  */
 interface IMediaMetricsManager {
     void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId);
+
     String getPlaybackSessionId(int userId);
     String getRecordingSessionId(int userId);
     void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId);
     void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId);
     void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId);
     void reportTrackChangeEvent(in String sessionId, in TrackChangeEvent event, int userId);
-}
\ No newline at end of file
+
+    String getTranscodingSessionId(int userId);
+    String getEditingSessionId(int userId);
+    String getBundleSessionId(int userId);
+    void reportBundleMetrics(in String sessionId, in PersistableBundle metrics, int userId);
+}
diff --git a/media/java/android/media/metrics/MediaMetricsManager.java b/media/java/android/media/metrics/MediaMetricsManager.java
index 23b697f..7229c47 100644
--- a/media/java/android/media/metrics/MediaMetricsManager.java
+++ b/media/java/android/media/metrics/MediaMetricsManager.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 
 /**
@@ -53,6 +54,17 @@
         }
     }
     /**
+     * Reports bundle metrics.
+     * @hide
+     */
+    public void reportBundleMetrics(@NonNull String sessionId, PersistableBundle metrics) {
+        try {
+            mService.reportBundleMetrics(sessionId, metrics, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+    /**
      * Reports network event.
      * @hide
      */
@@ -117,6 +129,48 @@
     }
 
     /**
+     * Creates a transcoding session.
+     */
+    @NonNull
+    public TranscodingSession createTranscodingSession() {
+        try {
+            String id = mService.getTranscodingSessionId(mUserId);
+            TranscodingSession session = new TranscodingSession(id, this);
+            return session;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a editing session.
+     */
+    @NonNull
+    public EditingSession createEditingSession() {
+        try {
+            String id = mService.getEditingSessionId(mUserId);
+            EditingSession session = new EditingSession(id, this);
+            return session;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a generic bundle session.
+     */
+    @NonNull
+    public BundleSession createBundleSession() {
+        try {
+            String id = mService.getBundleSessionId(mUserId);
+            BundleSession session = new BundleSession(id, this);
+            return session;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Reports error event.
      * @hide
      */
diff --git a/media/java/android/media/metrics/TranscodingSession.java b/media/java/android/media/metrics/TranscodingSession.java
new file mode 100644
index 0000000..e6d359a
--- /dev/null
+++ b/media/java/android/media/metrics/TranscodingSession.java
@@ -0,0 +1,63 @@
+/*
+ * 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.media.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.Objects;
+
+/**
+ * An instances of this class represents a session of media Transcoding.
+ */
+public final class TranscodingSession implements AutoCloseable {
+    private final @NonNull String mId;
+    private final @NonNull MediaMetricsManager mManager;
+    private final @NonNull LogSessionId mLogSessionId;
+
+    /** @hide */
+    public TranscodingSession(@NonNull String id, @NonNull MediaMetricsManager manager) {
+        mId = id;
+        mManager = manager;
+        AnnotationValidations.validate(NonNull.class, null, mId);
+        AnnotationValidations.validate(NonNull.class, null, mManager);
+        mLogSessionId = new LogSessionId(mId);
+    }
+
+    public @NonNull LogSessionId getSessionId() {
+        return mLogSessionId;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        TranscodingSession that = (TranscodingSession) o;
+        return Objects.equals(mId, that.mId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mId);
+    }
+
+    @Override
+    public void close() {
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 4e06ed7..bd3deae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -444,7 +444,8 @@
         }
         final Intent intent = new Intent()
                 .setComponent(dreamInfo.settingsComponentName)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                .addFlags(
+                        Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
         uiContext.startActivity(intent);
     }
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e9f940a..741fe4f 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -603,6 +603,9 @@
     <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
     <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
 
+    <!-- Permission required for CTS test - CtsAmbientContextDetectionServiceDeviceTest -->
+    <uses-permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" />
+
     <!-- Permission required for CTS test - CallAudioInterceptionTest -->
     <uses-permission android:name="android.permission.CALL_AUDIO_INTERCEPTION" />
 
diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
index b2857ab..bb6d4bd 100644
--- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
@@ -25,6 +25,7 @@
         android:layout_height="12dp"
         android:layout_gravity="left|bottom"
         android:tint="#ff000000"
+        android:visibility="gone"
         android:src="@drawable/rounded_corner_bottom"/>
 
     <ImageView
@@ -32,6 +33,7 @@
         android:layout_width="12dp"
         android:layout_height="12dp"
         android:tint="#ff000000"
+        android:visibility="gone"
         android:layout_gravity="right|bottom"
         android:src="@drawable/rounded_corner_bottom"/>
 
diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml
index 9937c21..46648c8 100644
--- a/packages/SystemUI/res/layout/rounded_corners_top.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_top.xml
@@ -25,6 +25,7 @@
         android:layout_height="12dp"
         android:layout_gravity="left|top"
         android:tint="#ff000000"
+        android:visibility="gone"
         android:src="@drawable/rounded_corner_top"/>
 
     <ImageView
@@ -32,6 +33,7 @@
         android:layout_width="12dp"
         android:layout_height="12dp"
         android:tint="#ff000000"
+        android:visibility="gone"
         android:layout_gravity="right|top"
         android:src="@drawable/rounded_corner_top"/>
 
diff --git a/packages/SystemUI/res/layout/screen_decor_hwc_layer.xml b/packages/SystemUI/res/layout/screen_decor_hwc_layer.xml
new file mode 100644
index 0000000..1c17e75
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_decor_hwc_layer.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<com.android.systemui.RegionInterceptingFrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/screen_decor_hwc_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
index 1633e52..2d883bc 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
@@ -21,7 +21,7 @@
     android:id="@+id/user_switcher_root"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_marginBottom="64dp"
+    android:layout_marginBottom="40dp"
     android:layout_marginEnd="60dp"
     android:layout_marginStart="60dp">
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5d252fd..b983545 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -754,11 +754,10 @@
         <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
     </style>
 
-    <style name="Theme.CreateUser" parent="@style/Theme.SystemUI">
+    <style name="Theme.CreateUser" parent="@android:style/Theme.DeviceDefault.NoActionBar">
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">#33000000</item>
-        <item name="android:windowActionBar">false</item>
-        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowFullscreen">true</item>
     </style>
 
     <style name="Theme.PeopleTileConfigActivity" parent="@style/Theme.SystemUI">
diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
new file mode 100644
index 0000000..6bec8aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.annotation.Dimension
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Matrix
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.Region
+import android.util.AttributeSet
+import android.view.Display
+import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.Surface
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView
+import com.android.systemui.animation.Interpolators
+
+/**
+ *  A class that handles common actions of display cutout view.
+ *  - Draws cutouts.
+ *  - Handles camera protection.
+ *  - Intercepts touches on cutout areas.
+ */
+open class DisplayCutoutBaseView : View, RegionInterceptableView {
+
+    private val shouldDrawCutout: Boolean = DisplayCutout.getFillBuiltInDisplayCutout(
+            context.resources, context.display?.uniqueId)
+    private var displayMode: Display.Mode? = null
+    private val location = IntArray(2)
+    protected var displayRotation = 0
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    @JvmField val displayInfo = DisplayInfo()
+    @JvmField protected var pendingRotationChange = false
+    @JvmField protected val paint = Paint()
+    @JvmField protected val cutoutPath = Path()
+
+    @JvmField protected var showProtection = false
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    @JvmField val protectionRect: RectF = RectF()
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    @JvmField val protectionPath: Path = Path()
+    private val protectionRectOrig: RectF = RectF()
+    private val protectionPathOrig: Path = Path()
+    private var cameraProtectionProgress: Float = HIDDEN_CAMERA_PROTECTION_SCALE
+    private var cameraProtectionAnimator: ValueAnimator? = null
+
+    constructor(context: Context) : super(context)
+
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
+            : super(context, attrs, defStyleAttr)
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+        updateCutout()
+    }
+
+    fun onDisplayChanged(displayId: Int) {
+        val oldMode: Display.Mode? = displayMode
+        displayMode = display.mode
+
+        // Skip if display mode or cutout hasn't changed.
+        if (!displayModeChanged(oldMode, displayMode) &&
+                display.cutout == displayInfo.displayCutout) {
+            return
+        }
+        if (displayId == display.displayId) {
+            updateCutout()
+            updateProtectionBoundingPath()
+        }
+    }
+
+    open fun updateRotation(rotation: Int) {
+        displayRotation = rotation
+        updateCutout()
+        updateProtectionBoundingPath()
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    public override fun onDraw(canvas: Canvas) {
+        super.onDraw(canvas)
+        if (!shouldDrawCutout) {
+            return
+        }
+        canvas.save()
+        getLocationOnScreen(location)
+        canvas.translate(-location[0].toFloat(), -location[1].toFloat())
+
+        drawCutouts(canvas)
+        drawCutoutProtection(canvas)
+        canvas.restore()
+    }
+
+    override fun shouldInterceptTouch(): Boolean {
+        return displayInfo.displayCutout != null && visibility == VISIBLE && shouldDrawCutout
+    }
+
+    override fun getInterceptRegion(): Region? {
+        displayInfo.displayCutout ?: return null
+
+        val cutoutBounds: Region = rectsToRegion(displayInfo.displayCutout?.boundingRects)
+        // Transform to window's coordinate space
+        rootView.getLocationOnScreen(location)
+        cutoutBounds.translate(-location[0], -location[1])
+
+        // Intersect with window's frame
+        cutoutBounds.op(rootView.left, rootView.top, rootView.right, rootView.bottom,
+                Region.Op.INTERSECT)
+        return cutoutBounds
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    open fun updateCutout() {
+        if (pendingRotationChange) {
+            return
+        }
+        cutoutPath.reset()
+        display.getDisplayInfo(displayInfo)
+        displayInfo.displayCutout?.cutoutPath?.let { path -> cutoutPath.set(path) }
+        invalidate()
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    open fun drawCutouts(canvas: Canvas) {
+        displayInfo.displayCutout?.cutoutPath ?: return
+        canvas.drawPath(cutoutPath, paint)
+    }
+
+    protected open fun drawCutoutProtection(canvas: Canvas) {
+        if (cameraProtectionProgress > HIDDEN_CAMERA_PROTECTION_SCALE &&
+                !protectionRect.isEmpty) {
+            canvas.scale(cameraProtectionProgress, cameraProtectionProgress,
+                    protectionRect.centerX(), protectionRect.centerY())
+            canvas.drawPath(protectionPath, paint)
+        }
+    }
+
+    /**
+     * Converts a set of [Rect]s into a [Region]
+     */
+    fun rectsToRegion(rects: List<Rect?>?): Region {
+        val result = Region.obtain()
+        if (rects != null) {
+            for (r in rects) {
+                if (r != null && !r.isEmpty) {
+                    result.op(r, Region.Op.UNION)
+                }
+            }
+        }
+        return result
+    }
+
+    open fun enableShowProtection(show: Boolean) {
+        if (showProtection == show) {
+            return
+        }
+        showProtection = show
+        updateProtectionBoundingPath()
+        // Delay the relayout until the end of the animation when hiding the cutout,
+        // otherwise we'd clip it.
+        if (showProtection) {
+            requestLayout()
+        }
+        cameraProtectionAnimator?.cancel()
+        cameraProtectionAnimator = ValueAnimator.ofFloat(cameraProtectionProgress,
+                if (showProtection) 1.0f else HIDDEN_CAMERA_PROTECTION_SCALE).setDuration(750)
+        cameraProtectionAnimator?.interpolator = Interpolators.DECELERATE_QUINT
+        cameraProtectionAnimator?.addUpdateListener(ValueAnimator.AnimatorUpdateListener {
+            animation: ValueAnimator ->
+            cameraProtectionProgress = animation.animatedValue as Float
+            invalidate()
+        })
+        cameraProtectionAnimator?.addListener(object : AnimatorListenerAdapter() {
+            override fun onAnimationEnd(animation: Animator) {
+                cameraProtectionAnimator = null
+                if (!showProtection) {
+                    requestLayout()
+                }
+            }
+        })
+        cameraProtectionAnimator?.start()
+    }
+
+    open fun setProtection(path: Path, pathBounds: Rect) {
+        protectionPathOrig.reset()
+        protectionPathOrig.set(path)
+        protectionPath.reset()
+        protectionRectOrig.setEmpty()
+        protectionRectOrig.set(pathBounds)
+        protectionRect.setEmpty()
+    }
+
+    protected open fun updateProtectionBoundingPath() {
+        if (pendingRotationChange) {
+            return
+        }
+        val lw: Int = displayInfo.logicalWidth
+        val lh: Int = displayInfo.logicalHeight
+        val flipped = (displayInfo.rotation == Surface.ROTATION_90 ||
+                displayInfo.rotation == Surface.ROTATION_270)
+        val dw = if (flipped) lh else lw
+        val dh = if (flipped) lw else lh
+        val m = Matrix()
+        transformPhysicalToLogicalCoordinates(displayInfo.rotation, dw, dh, m)
+        if (!protectionPathOrig.isEmpty) {
+            // Reset the protection path so we don't aggregate rotations
+            protectionPath.set(protectionPathOrig)
+            protectionPath.transform(m)
+            m.mapRect(protectionRect, protectionRectOrig)
+        }
+    }
+
+    private fun displayModeChanged(oldMode: Display.Mode?, newMode: Display.Mode?): Boolean {
+        if (oldMode == null) {
+            return true
+        }
+
+        // We purposely ignore refresh rate and id changes here, because we don't need to
+        // invalidate for those, and they can trigger the refresh rate to increase
+        return oldMode?.physicalHeight != newMode?.physicalHeight ||
+                oldMode?.physicalWidth != newMode?.physicalWidth
+    }
+
+    companion object {
+        private const val HIDDEN_CAMERA_PROTECTION_SCALE = 0.5f
+
+        @JvmStatic protected fun transformPhysicalToLogicalCoordinates(
+            @Surface.Rotation rotation: Int,
+            @Dimension physicalWidth: Int,
+            @Dimension physicalHeight: Int,
+            out: Matrix
+        ) {
+            when (rotation) {
+                Surface.ROTATION_0 -> out.reset()
+                Surface.ROTATION_90 -> {
+                    out.setRotate(270f)
+                    out.postTranslate(0f, physicalWidth.toFloat())
+                }
+                Surface.ROTATION_180 -> {
+                    out.setRotate(180f)
+                    out.postTranslate(physicalWidth.toFloat(), physicalHeight.toFloat())
+                }
+                Surface.ROTATION_270 -> {
+                    out.setRotate(90f)
+                    out.postTranslate(physicalHeight.toFloat(), 0f)
+                }
+                else -> throw IllegalArgumentException("Unknown rotation: $rotation")
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
new file mode 100644
index 0000000..ee1d9a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorFilter
+import android.graphics.Paint
+import android.graphics.PixelFormat
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.graphics.PorterDuffXfermode
+import android.graphics.drawable.Drawable
+import android.hardware.graphics.common.AlphaInterpretation
+import android.hardware.graphics.common.DisplayDecorationSupport
+import android.view.RoundedCorner
+import android.view.RoundedCorners
+
+/**
+ * When the HWC of the device supports Composition.DISPLAY_DECORATON, we use this layer to draw
+ * screen decorations.
+ */
+class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDecorationSupport)
+    : DisplayCutoutBaseView(context) {
+    public val colorMode: Int
+    private val useInvertedAlphaColor: Boolean
+    private val color: Int
+    private val bgColor: Int
+    private val cornerFilter: ColorFilter
+    private val cornerBgFilter: ColorFilter
+    private val clearPaint: Paint
+
+    private var roundedCornerTopSize = 0
+    private var roundedCornerBottomSize = 0
+    private var roundedCornerDrawableTop: Drawable? = null
+    private var roundedCornerDrawableBottom: Drawable? = null
+
+    init {
+        if (displayDecorationSupport.format != PixelFormat.R_8) {
+            throw IllegalArgumentException("Attempting to use unsupported mode " +
+                    "${PixelFormat.formatToString(displayDecorationSupport.format)}")
+        }
+        if (DEBUG_COLOR) {
+            color = Color.GREEN
+            bgColor = Color.TRANSPARENT
+            colorMode = ActivityInfo.COLOR_MODE_DEFAULT
+            useInvertedAlphaColor = false
+        } else {
+            colorMode = ActivityInfo.COLOR_MODE_A8
+            useInvertedAlphaColor = displayDecorationSupport.alphaInterpretation ==
+                    AlphaInterpretation.COVERAGE
+            if (useInvertedAlphaColor) {
+                color = Color.TRANSPARENT
+                bgColor = Color.BLACK
+            } else {
+                color = Color.BLACK
+                bgColor = Color.TRANSPARENT
+            }
+        }
+        cornerFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
+        cornerBgFilter = PorterDuffColorFilter(bgColor, PorterDuff.Mode.SRC_OUT)
+
+        clearPaint = Paint()
+        clearPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
+    }
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+        viewRootImpl.setDisplayDecoration(true)
+
+        if (useInvertedAlphaColor) {
+            paint.set(clearPaint)
+        } else {
+            paint.color = color
+            paint.style = Paint.Style.FILL
+        }
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        if (useInvertedAlphaColor) {
+            canvas.drawColor(bgColor)
+        }
+        // Cutouts are drawn in DisplayCutoutBaseView.onDraw()
+        super.onDraw(canvas)
+        drawRoundedCorners(canvas)
+    }
+
+    private fun drawRoundedCorners(canvas: Canvas) {
+        if (roundedCornerTopSize == 0 && roundedCornerBottomSize == 0) {
+            return
+        }
+        var degree: Int
+        for (i in RoundedCorner.POSITION_TOP_LEFT
+                until RoundedCorners.ROUNDED_CORNER_POSITION_LENGTH) {
+            canvas.save()
+            degree = getRoundedCornerRotationDegree(90 * i)
+            canvas.rotate(degree.toFloat())
+            canvas.translate(
+                    getRoundedCornerTranslationX(degree).toFloat(),
+                    getRoundedCornerTranslationY(degree).toFloat())
+            if (i == RoundedCorner.POSITION_TOP_LEFT || i == RoundedCorner.POSITION_TOP_RIGHT) {
+                drawRoundedCorner(canvas, roundedCornerDrawableTop, roundedCornerTopSize)
+            } else {
+                drawRoundedCorner(canvas, roundedCornerDrawableBottom, roundedCornerBottomSize)
+            }
+            canvas.restore()
+        }
+    }
+
+    private fun drawRoundedCorner(canvas: Canvas, drawable: Drawable?, size: Int) {
+        if (useInvertedAlphaColor) {
+            canvas.drawRect(0f, 0f, size.toFloat(), size.toFloat(), clearPaint)
+            drawable?.colorFilter = cornerBgFilter
+        } else {
+            drawable?.colorFilter = cornerFilter
+        }
+        drawable?.draw(canvas)
+        // Clear color filter when we are done with drawing.
+        drawable?.clearColorFilter()
+    }
+
+    private fun getRoundedCornerRotationDegree(defaultDegree: Int): Int {
+        return (defaultDegree - 90 * displayRotation + 360) % 360
+    }
+
+    private fun getRoundedCornerTranslationX(degree: Int): Int {
+        return when (degree) {
+            0, 90 -> 0
+            180 -> -width
+            270 -> -height
+            else -> throw IllegalArgumentException("Incorrect degree: $degree")
+        }
+    }
+
+    private fun getRoundedCornerTranslationY(degree: Int): Int {
+        return when (degree) {
+            0, 270 -> 0
+            90 -> -width
+            180 -> -height
+            else -> throw IllegalArgumentException("Incorrect degree: $degree")
+        }
+    }
+
+    /**
+     * Update the rounded corner drawables.
+     */
+    fun updateRoundedCornerDrawable(top: Drawable, bottom: Drawable) {
+        roundedCornerDrawableTop = top
+        roundedCornerDrawableBottom = bottom
+        updateRoundedCornerDrawableBounds()
+        invalidate()
+    }
+
+    /**
+     * Update the rounded corner size.
+     */
+    fun updateRoundedCornerSize(top: Int, bottom: Int) {
+        roundedCornerTopSize = top
+        roundedCornerBottomSize = bottom
+        updateRoundedCornerDrawableBounds()
+        invalidate()
+    }
+
+    private fun updateRoundedCornerDrawableBounds() {
+        if (roundedCornerDrawableTop != null) {
+            roundedCornerDrawableTop?.setBounds(0, 0, roundedCornerTopSize,
+                    roundedCornerTopSize)
+        }
+        if (roundedCornerDrawableBottom != null) {
+            roundedCornerDrawableBottom?.setBounds(0, 0, roundedCornerBottomSize,
+                    roundedCornerBottomSize)
+        }
+        invalidate()
+    }
+
+    companion object {
+        private val DEBUG_COLOR = ScreenDecorations.DEBUG_COLOR
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 783415e..2ec5f4f 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -14,24 +14,17 @@
 
 package com.android.systemui;
 
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
 import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
 import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
 import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
-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 static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.Dimension;
 import android.annotation.IdRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -40,11 +33,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -52,10 +45,10 @@
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
+import android.hardware.graphics.common.AlphaInterpretation;
+import android.hardware.graphics.common.DisplayDecorationSupport;
 import android.os.Handler;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -63,14 +56,11 @@
 import android.util.DisplayMetrics;
 import android.util.DisplayUtils;
 import android.util.Log;
-import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.DisplayCutout.BoundsPosition;
-import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.RoundedCorners;
-import android.view.Surface;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
@@ -83,8 +73,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.util.Preconditions;
-import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView;
-import com.android.systemui.animation.Interpolators;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -106,6 +94,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -117,7 +106,7 @@
  * for antialiasing and emulation purposes.
  */
 @SysUISingleton
-public class ScreenDecorations extends CoreStartable implements Tunable , Dumpable{
+public class ScreenDecorations extends CoreStartable implements Tunable , Dumpable {
     private static final boolean DEBUG = false;
     private static final String TAG = "ScreenDecorations";
 
@@ -129,7 +118,7 @@
     private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
             SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
     private static final boolean VERBOSE = false;
-    private static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS;
+    static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS;
 
     private DisplayManager mDisplayManager;
     @VisibleForTesting
@@ -138,7 +127,8 @@
     private final Executor mMainExecutor;
     private final TunerService mTunerService;
     private final SecureSettings mSecureSettings;
-    private DisplayManager.DisplayListener mDisplayListener;
+    @VisibleForTesting
+    DisplayManager.DisplayListener mDisplayListener;
     private CameraAvailabilityListener mCameraListener;
     private final UserTracker mUserTracker;
     private final PrivacyDotViewController mDotViewController;
@@ -158,23 +148,36 @@
     protected OverlayWindow[] mOverlays = null;
     @Nullable
     private DisplayCutoutView[] mCutoutViews;
+    @VisibleForTesting
+    ViewGroup mScreenDecorHwcWindow;
+    @VisibleForTesting
+    ScreenDecorHwcLayer mScreenDecorHwcLayer;
     private float mDensity;
     private WindowManager mWindowManager;
     private int mRotation;
     private SettingObserver mColorInversionSetting;
     private DelayableExecutor mExecutor;
     private Handler mHandler;
-    private boolean mPendingRotationChange;
+    boolean mPendingRotationChange;
     private boolean mIsRoundedCornerMultipleRadius;
     private Drawable mRoundedCornerDrawable;
     private Drawable mRoundedCornerDrawableTop;
     private Drawable mRoundedCornerDrawableBottom;
-    private String mDisplayUniqueId;
+    @VisibleForTesting
+    String mDisplayUniqueId;
+    private int mTintColor = Color.BLACK;
+    @VisibleForTesting
+    protected DisplayDecorationSupport mHwcScreenDecorationSupport;
 
     private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
             new CameraAvailabilityListener.CameraTransitionCallback() {
         @Override
         public void onApplyCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) {
+            if (mScreenDecorHwcLayer != null) {
+                mScreenDecorHwcLayer.setProtection(protectionPath, bounds);
+                mScreenDecorHwcLayer.enableShowProtection(true);
+                return;
+            }
             if (mCutoutViews == null) {
                 Log.w(TAG, "DisplayCutoutView do not initialized");
                 return;
@@ -184,13 +187,17 @@
                 // Check Null since not all mCutoutViews[pos] be inflated at the meanwhile
                 if (dcv != null) {
                     dcv.setProtection(protectionPath, bounds);
-                    dcv.setShowProtection(true);
+                    dcv.enableShowProtection(true);
                 }
             }
         }
 
         @Override
         public void onHideCameraProtection() {
+            if (mScreenDecorHwcLayer != null) {
+                mScreenDecorHwcLayer.enableShowProtection(false);
+                return;
+            }
             if (mCutoutViews == null) {
                 Log.w(TAG, "DisplayCutoutView do not initialized");
                 return;
@@ -199,27 +206,59 @@
             for (DisplayCutoutView dcv : mCutoutViews) {
                 // Check Null since not all mCutoutViews[pos] be inflated at the meanwhile
                 if (dcv != null) {
-                    dcv.setShowProtection(false);
+                    dcv.enableShowProtection(false);
                 }
             }
         }
     };
 
-    /**
-     * Converts a set of {@link Rect}s into a {@link Region}
-     *
-     * @hide
-     */
-    public static Region rectsToRegion(List<Rect> rects) {
-        Region result = Region.obtain();
-        if (rects != null) {
-            for (Rect r : rects) {
-                if (r != null && !r.isEmpty()) {
-                    result.op(r, Region.Op.UNION);
-                }
+    private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener =
+            new PrivacyDotViewController.ShowingListener() {
+        @Override
+        public void onPrivacyDotShown(@Nullable View v) {
+            // We don't need to control the window visibility when the hwc doesn't support screen
+            // decoration since the overlay windows are always visible in this case.
+            if (mHwcScreenDecorationSupport == null || v == null) {
+                return;
             }
+            mExecutor.execute(() -> {
+                for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
+                    if (mOverlays[i] == null) {
+                        continue;
+                    }
+                    final ViewGroup overlayView = mOverlays[i].getRootView();
+                    if (overlayView.findViewById(v.getId()) != null) {
+                        overlayView.setVisibility(View.VISIBLE);
+                    }
+                }
+            });
         }
-        return result;
+
+        @Override
+        public void onPrivacyDotHidden(@Nullable View v) {
+            // We don't need to control the window visibility when the hwc doesn't support screen
+            // decoration since the overlay windows are always visible in this case.
+            if (mHwcScreenDecorationSupport == null || v == null) {
+                return;
+            }
+            mExecutor.execute(() -> {
+                for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
+                    if (mOverlays[i] == null) {
+                        continue;
+                    }
+                    final ViewGroup overlayView = mOverlays[i].getRootView();
+                    if (overlayView.findViewById(v.getId()) != null) {
+                        overlayView.setVisibility(View.INVISIBLE);
+                    }
+                }
+            });
+        }
+    };
+
+    private static boolean eq(DisplayDecorationSupport a, DisplayDecorationSupport b) {
+        if (a == null) return (b == null);
+        if (b == null) return false;
+        return a.format == b.format && a.alphaInterpretation == b.alphaInterpretation;
     }
 
     @Inject
@@ -241,6 +280,7 @@
         mDotViewController = dotViewController;
         mThreadFactory = threadFactory;
         mDotFactory = dotFactory;
+        dotViewController.setShowingListener(mPrivacyDotShowingListener);
     }
 
     @Override
@@ -265,6 +305,7 @@
         mIsRoundedCornerMultipleRadius = isRoundedCornerMultipleRadius(mContext, mDisplayUniqueId);
         mWindowManager = mContext.getSystemService(WindowManager.class);
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
+        mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport();
         updateRoundedCornerDrawable();
         updateRoundedCornerRadii();
         setupDecorations();
@@ -305,15 +346,36 @@
                                     new RestartingPreDrawListener(overlayView, i, newRotation));
                         }
                     }
+
+                    if (mScreenDecorHwcWindow != null) {
+                        mScreenDecorHwcWindow.getViewTreeObserver().addOnPreDrawListener(
+                                new RestartingPreDrawListener(
+                                        mScreenDecorHwcWindow,
+                                        -1, // Pass -1 for views with no specific position.
+                                        newRotation));
+                    }
                 }
+
                 final String newUniqueId = mContext.getDisplay().getUniqueId();
-                if ((newUniqueId != null && !newUniqueId.equals(mDisplayUniqueId))
-                        || (mDisplayUniqueId != null && !mDisplayUniqueId.equals(newUniqueId))) {
+                if (!Objects.equals(newUniqueId, mDisplayUniqueId)) {
                     mDisplayUniqueId = newUniqueId;
                     mIsRoundedCornerMultipleRadius =
                             isRoundedCornerMultipleRadius(mContext, mDisplayUniqueId);
+                    final DisplayDecorationSupport newScreenDecorationSupport =
+                            mContext.getDisplay().getDisplayDecorationSupport();
+                    // When the value of mSupportHwcScreenDecoration is changed, re-setup the whole
+                    // screen decoration.
+                    if (!eq(newScreenDecorationSupport, mHwcScreenDecorationSupport)) {
+                        mHwcScreenDecorationSupport = newScreenDecorationSupport;
+                        removeAllOverlays();
+                        setupDecorations();
+                        return;
+                    }
                     updateRoundedCornerDrawable();
                 }
+                if (mScreenDecorHwcLayer != null) {
+                    mScreenDecorHwcLayer.onDisplayChanged(displayId);
+                }
                 updateOrientation();
             }
         };
@@ -359,6 +421,11 @@
         List<DecorProvider> decorProviders = mDotFactory.getProviders();
 
         if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) {
+            if (mHwcScreenDecorationSupport != null) {
+                createHwcOverlay();
+            } else {
+                removeHwcOverlay();
+            }
             final DisplayCutout cutout = getCutout();
             for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
                 if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout)
@@ -383,14 +450,15 @@
             }
         } else {
             removeAllOverlays();
+            removeHwcOverlay();
         }
 
-        if (hasOverlays()) {
+        if (hasOverlays() || hasHwcOverlay()) {
             if (mIsRegistered) {
                 return;
             }
             DisplayMetrics metrics = new DisplayMetrics();
-            mDisplayManager.getDisplay(DEFAULT_DISPLAY).getMetrics(metrics);
+            mContext.getDisplay().getMetrics(metrics);
             mDensity = metrics.density;
 
             mMainExecutor.execute(() -> mTunerService.addTunable(this, SIZE));
@@ -475,25 +543,27 @@
             mOverlays = new OverlayWindow[BOUNDS_POSITION_LENGTH];
         }
 
-        if (mCutoutViews == null) {
-            mCutoutViews = new DisplayCutoutView[BOUNDS_POSITION_LENGTH];
-        }
-
         if (mOverlays[pos] != null) {
             return;
         }
 
         mOverlays[pos] = overlayForPosition(pos, decorProviders);
-
-        mCutoutViews[pos] = new DisplayCutoutView(mContext, pos, this);
-        mOverlays[pos].getRootView().addView(mCutoutViews[pos]);
-
         final ViewGroup overlayView = mOverlays[pos].getRootView();
         overlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
         overlayView.setAlpha(0);
         overlayView.setForceDarkAllowed(false);
 
-        updateView(pos, cutout);
+        // Only show cutout and rounded corners in mOverlays when hwc don't support screen
+        // decoration.
+        if (mHwcScreenDecorationSupport == null) {
+            if (mCutoutViews == null) {
+                mCutoutViews = new DisplayCutoutView[BOUNDS_POSITION_LENGTH];
+            }
+            mCutoutViews[pos] = new DisplayCutoutView(mContext, pos);
+            mCutoutViews[pos].setColor(mTintColor);
+            overlayView.addView(mCutoutViews[pos]);
+            updateView(pos, cutout);
+        }
 
         mWindowManager.addView(overlayView, getWindowLayoutParams(pos));
 
@@ -509,8 +579,37 @@
             }
         });
 
-        mOverlays[pos].getRootView().getViewTreeObserver().addOnPreDrawListener(
-                new ValidatingPreDrawListener(mOverlays[pos].getRootView()));
+        overlayView.getRootView().getViewTreeObserver().addOnPreDrawListener(
+                new ValidatingPreDrawListener(overlayView.getRootView()));
+    }
+
+    private boolean hasHwcOverlay() {
+        return mScreenDecorHwcWindow != null;
+    }
+
+    private void removeHwcOverlay() {
+        if (mScreenDecorHwcWindow == null) {
+            return;
+        }
+        mWindowManager.removeViewImmediate(mScreenDecorHwcWindow);
+        mScreenDecorHwcWindow = null;
+        mScreenDecorHwcLayer = null;
+    }
+
+    private void createHwcOverlay() {
+        if (mScreenDecorHwcWindow != null) {
+            return;
+        }
+        mScreenDecorHwcWindow = (ViewGroup) LayoutInflater.from(mContext).inflate(
+                R.layout.screen_decor_hwc_layer, null);
+        mScreenDecorHwcLayer = new ScreenDecorHwcLayer(mContext, mHwcScreenDecorationSupport);
+        mScreenDecorHwcWindow.addView(mScreenDecorHwcLayer, new FrameLayout.LayoutParams(
+                MATCH_PARENT, MATCH_PARENT, Gravity.TOP | Gravity.START));
+        mWindowManager.addView(mScreenDecorHwcWindow, getHwcWindowLayoutParams());
+        updateRoundedCornerSize(mRoundedDefault, mRoundedDefaultTop, mRoundedDefaultBottom);
+        updateRoundedCornerImageView();
+        mScreenDecorHwcWindow.getViewTreeObserver().addOnPreDrawListener(
+                new ValidatingPreDrawListener(mScreenDecorHwcWindow));
     }
 
     /**
@@ -523,12 +622,18 @@
         decorProviders.forEach(provider -> {
             removeOverlayView(provider.getViewId());
             currentOverlay.addDecorProvider(provider, mRotation);
+            // If the hwc supports screen decoration and privacy dot is enabled, it means there will
+            // be only privacy dot in mOverlay. So set the initial visibility of mOverlays to
+            // INVISIBLE and will only set it to VISIBLE when the privacy dot is showing.
+            if (mHwcScreenDecorationSupport != null) {
+                currentOverlay.getRootView().setVisibility(View.INVISIBLE);
+            }
         });
         return currentOverlay;
     }
 
     private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
-        if (mOverlays == null || mOverlays[pos] == null) {
+        if (mOverlays == null || mOverlays[pos] == null || mHwcScreenDecorationSupport != null) {
             return;
         }
 
@@ -540,15 +645,34 @@
 
         // update cutout view rotation
         if (mCutoutViews != null && mCutoutViews[pos] != null) {
-            mCutoutViews[pos].setRotation(mRotation);
+            mCutoutViews[pos].updateRotation(mRotation);
         }
     }
 
     @VisibleForTesting
     WindowManager.LayoutParams getWindowLayoutParams(@BoundsPosition int pos) {
+        final WindowManager.LayoutParams lp = getWindowLayoutBaseParams();
+        lp.width = getWidthLayoutParamByPos(pos);
+        lp.height = getHeightLayoutParamByPos(pos);
+        lp.setTitle(getWindowTitleByPos(pos));
+        lp.gravity = getOverlayWindowGravity(pos);
+        return lp;
+    }
+
+    private WindowManager.LayoutParams getHwcWindowLayoutParams() {
+        final WindowManager.LayoutParams lp = getWindowLayoutBaseParams();
+        lp.width = MATCH_PARENT;
+        lp.height = MATCH_PARENT;
+        lp.setTitle("ScreenDecorHwcOverlay");
+        lp.gravity = Gravity.TOP | Gravity.START;
+        if (!DEBUG_COLOR) {
+            lp.setColorMode(ActivityInfo.COLOR_MODE_A8);
+        }
+        return lp;
+    }
+
+    private WindowManager.LayoutParams getWindowLayoutBaseParams() {
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                getWidthLayoutParamByPos(pos),
-                getHeightLayoutParamByPos(pos),
                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -566,8 +690,6 @@
             lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
         }
 
-        lp.setTitle(getWindowTitleByPos(pos));
-        lp.gravity = getOverlayWindowGravity(pos);
         lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         lp.setFitInsetsTypes(0 /* types */);
         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
@@ -648,15 +770,19 @@
     };
 
     private void updateColorInversion(int colorsInvertedValue) {
-        int tint = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
+        mTintColor = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
         if (DEBUG_COLOR) {
-            tint = Color.RED;
+            mTintColor = Color.RED;
         }
-        ColorStateList tintList = ColorStateList.valueOf(tint);
 
-        if (mOverlays == null) {
+        // When the hwc supports screen decorations, the layer will use the A8 color mode which
+        // won't be affected by the color inversion. If the composition goes the client composition
+        // route, the color inversion will be handled by the RenderEngine.
+        if (mOverlays == null || mHwcScreenDecorationSupport != null) {
             return;
         }
+
+        ColorStateList tintList = ColorStateList.valueOf(mTintColor);
         for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
             if (mOverlays[i] == null) {
                 continue;
@@ -676,7 +802,7 @@
                 if (child instanceof ImageView) {
                     ((ImageView) child).setImageTintList(tintList);
                 } else if (child instanceof DisplayCutoutView) {
-                    ((DisplayCutoutView) child).setColor(tint);
+                    ((DisplayCutoutView) child).setColor(mTintColor);
                 }
             }
         }
@@ -688,6 +814,7 @@
             Log.i(TAG, "ScreenDecorations is disabled");
             return;
         }
+
         mExecutor.execute(() -> {
             int oldRotation = mRotation;
             mPendingRotationChange = false;
@@ -705,6 +832,14 @@
         });
     }
 
+    private static String alphaInterpretationToString(int alpha) {
+        switch (alpha) {
+            case AlphaInterpretation.COVERAGE: return "COVERAGE";
+            case AlphaInterpretation.MASK:     return "MASK";
+            default:                           return "Unknown: " + alpha;
+        }
+    }
+
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("ScreenDecorations state:");
@@ -712,6 +847,15 @@
         pw.println("  mIsRoundedCornerMultipleRadius:" + mIsRoundedCornerMultipleRadius);
         pw.println("  mIsPrivacyDotEnabled:" + isPrivacyDotEnabled());
         pw.println("  mPendingRotationChange:" + mPendingRotationChange);
+        pw.println("  mHwcScreenDecorationSupport:");
+        if (mHwcScreenDecorationSupport == null) {
+            pw.println("    null");
+        } else {
+            pw.println("    format: "
+                    + PixelFormat.formatToString(mHwcScreenDecorationSupport.format));
+            pw.println("    alphaInterpretation: "
+                    + alphaInterpretationToString(mHwcScreenDecorationSupport.alphaInterpretation));
+        }
         pw.println("  mRoundedDefault(x,y)=(" + mRoundedDefault.x + "," + mRoundedDefault.y + ")");
         pw.println("  mRoundedDefaultTop(x,y)=(" + mRoundedDefaultTop.x + "," + mRoundedDefaultTop.y
                 + ")");
@@ -739,7 +883,10 @@
         }
         if (newRotation != mRotation) {
             mRotation = newRotation;
-
+            if (mScreenDecorHwcLayer != null) {
+                mScreenDecorHwcLayer.pendingRotationChange = false;
+                mScreenDecorHwcLayer.updateRotation(mRotation);
+            }
             if (mOverlays != null) {
                 updateLayoutParams();
                 final DisplayCutout cutout = getCutout();
@@ -956,7 +1103,8 @@
 
     private boolean shouldShowRoundedCorner(@BoundsPosition int pos,
             @Nullable DisplayCutout cutout) {
-        return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout);
+        return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout)
+                && mHwcScreenDecorationSupport == null;
     }
 
     private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
@@ -966,7 +1114,8 @@
     private boolean shouldShowCutout(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
         final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
         final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
-        return (bounds != null && !bounds[rotatedPos].isEmpty());
+        return (bounds != null && !bounds[rotatedPos].isEmpty()
+                && mHwcScreenDecorationSupport == null);
     }
 
     private boolean shouldDrawCutout() {
@@ -1027,14 +1176,22 @@
         final Drawable bottom = mRoundedCornerDrawableBottom != null
                 ? mRoundedCornerDrawableBottom : mRoundedCornerDrawable;
 
+        if (mScreenDecorHwcLayer != null) {
+            mScreenDecorHwcLayer.updateRoundedCornerDrawable(top, bottom);
+            return;
+        }
+
         if (mOverlays == null) {
             return;
         }
+        final ColorStateList colorStateList = ColorStateList.valueOf(mTintColor);
         for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
             if (mOverlays[i] == null) {
                 continue;
             }
             final ViewGroup overlayView = mOverlays[i].getRootView();
+            ((ImageView) overlayView.findViewById(R.id.left)).setImageTintList(colorStateList);
+            ((ImageView) overlayView.findViewById(R.id.right)).setImageTintList(colorStateList);
             ((ImageView) overlayView.findViewById(R.id.left)).setImageDrawable(
                     isTopRoundedCorner(i, R.id.left) ? top : bottom);
             ((ImageView) overlayView.findViewById(R.id.right)).setImageDrawable(
@@ -1065,9 +1222,6 @@
             Point sizeDefault,
             Point sizeTop,
             Point sizeBottom) {
-        if (mOverlays == null) {
-            return;
-        }
         if (sizeTop.x == 0) {
             sizeTop = sizeDefault;
         }
@@ -1075,6 +1229,14 @@
             sizeBottom = sizeDefault;
         }
 
+        if (mScreenDecorHwcLayer != null) {
+            mScreenDecorHwcLayer.updateRoundedCornerSize(sizeTop.x, sizeBottom.x);
+            return;
+        }
+
+        if (mOverlays == null) {
+            return;
+        }
         for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
             if (mOverlays[i] == null) {
                 continue;
@@ -1095,40 +1257,21 @@
         view.setLayoutParams(params);
     }
 
-    public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener,
-            RegionInterceptableView {
-
-        private static final float HIDDEN_CAMERA_PROTECTION_SCALE = 0.5f;
-
-        private Display.Mode mDisplayMode = null;
-        private final DisplayInfo mInfo = new DisplayInfo();
-        private final Paint mPaint = new Paint();
+    public static class DisplayCutoutView extends DisplayCutoutBaseView {
         private final List<Rect> mBounds = new ArrayList();
         private final Rect mBoundingRect = new Rect();
-        private final Path mBoundingPath = new Path();
-        // Don't initialize these yet because they may never exist
-        private RectF mProtectionRect;
-        private RectF mProtectionRectOrig;
-        private Path mProtectionPath;
-        private Path mProtectionPathOrig;
         private Rect mTotalBounds = new Rect();
-        // Whether or not to show the cutout protection path
-        private boolean mShowProtection = false;
 
-        private final int[] mLocation = new int[2];
-        private final ScreenDecorations mDecorations;
         private int mColor = Color.BLACK;
         private int mRotation;
         private int mInitialPosition;
         private int mPosition;
-        private float mCameraProtectionProgress = HIDDEN_CAMERA_PROTECTION_SCALE;
-        private ValueAnimator mCameraProtectionAnimator;
 
-        public DisplayCutoutView(Context context, @BoundsPosition int pos,
-                ScreenDecorations decorations) {
+        public DisplayCutoutView(Context context, @BoundsPosition int pos) {
             super(context);
             mInitialPosition = pos;
-            mDecorations = decorations;
+            paint.setColor(mColor);
+            paint.setStyle(Paint.Style.FILL);
             setId(R.id.display_cutout);
             if (DEBUG) {
                 getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG,
@@ -1138,145 +1281,31 @@
 
         public void setColor(int color) {
             mColor = color;
+            paint.setColor(mColor);
             invalidate();
         }
 
         @Override
-        protected void onAttachedToWindow() {
-            super.onAttachedToWindow();
-            mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
-                    getHandler());
-            update();
-        }
-
-        @Override
-        protected void onDetachedFromWindow() {
-            super.onDetachedFromWindow();
-            mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
-        }
-
-        @Override
-        protected void onDraw(Canvas canvas) {
-            super.onDraw(canvas);
-            getLocationOnScreen(mLocation);
-            canvas.translate(-mLocation[0], -mLocation[1]);
-
-            if (!mBoundingPath.isEmpty()) {
-                mPaint.setColor(mColor);
-                mPaint.setStyle(Paint.Style.FILL);
-                mPaint.setAntiAlias(true);
-                canvas.drawPath(mBoundingPath, mPaint);
-            }
-            if (mCameraProtectionProgress > HIDDEN_CAMERA_PROTECTION_SCALE
-                    && !mProtectionRect.isEmpty()) {
-                canvas.scale(mCameraProtectionProgress, mCameraProtectionProgress,
-                        mProtectionRect.centerX(), mProtectionRect.centerY());
-                canvas.drawPath(mProtectionPath, mPaint);
-            }
-        }
-
-        @Override
-        public void onDisplayAdded(int displayId) {
-        }
-
-        @Override
-        public void onDisplayRemoved(int displayId) {
-        }
-
-        @Override
-        public void onDisplayChanged(int displayId) {
-            Display.Mode oldMode = mDisplayMode;
-            mDisplayMode = getDisplay().getMode();
-
-            // Display mode hasn't meaningfully changed, we can ignore it
-            if (!modeChanged(oldMode, mDisplayMode)) {
-                return;
-            }
-
-            if (displayId == getDisplay().getDisplayId()) {
-                update();
-            }
-        }
-
-        private boolean modeChanged(Display.Mode oldMode, Display.Mode newMode) {
-            if (oldMode == null) {
-                return true;
-            }
-
-            boolean changed = false;
-            changed |= oldMode.getPhysicalHeight() != newMode.getPhysicalHeight();
-            changed |= oldMode.getPhysicalWidth() != newMode.getPhysicalWidth();
-            // We purposely ignore refresh rate and id changes here, because we don't need to
-            // invalidate for those, and they can trigger the refresh rate to increase
-
-            return changed;
-        }
-
-        public void setRotation(int rotation) {
+        public void updateRotation(int rotation) {
             mRotation = rotation;
-            update();
+            updateCutout();
         }
 
-        void setProtection(Path protectionPath, Rect pathBounds) {
-            if (mProtectionPathOrig == null) {
-                mProtectionPathOrig = new Path();
-                mProtectionPath = new Path();
-            }
-            mProtectionPathOrig.set(protectionPath);
-            if (mProtectionRectOrig == null) {
-                mProtectionRectOrig = new RectF();
-                mProtectionRect = new RectF();
-            }
-            mProtectionRectOrig.set(pathBounds);
-        }
-
-        void setShowProtection(boolean shouldShow) {
-            if (mShowProtection == shouldShow) {
-                return;
-            }
-
-            mShowProtection = shouldShow;
-            updateBoundingPath();
-            // Delay the relayout until the end of the animation when hiding the cutout,
-            // otherwise we'd clip it.
-            if (mShowProtection) {
-                requestLayout();
-            }
-            if (mCameraProtectionAnimator != null) {
-                mCameraProtectionAnimator.cancel();
-            }
-            mCameraProtectionAnimator = ValueAnimator.ofFloat(mCameraProtectionProgress,
-                    mShowProtection ? 1.0f : HIDDEN_CAMERA_PROTECTION_SCALE).setDuration(750);
-            mCameraProtectionAnimator.setInterpolator(Interpolators.DECELERATE_QUINT);
-            mCameraProtectionAnimator.addUpdateListener(animation -> {
-                mCameraProtectionProgress = (float) animation.getAnimatedValue();
-                invalidate();
-            });
-            mCameraProtectionAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mCameraProtectionAnimator = null;
-                    if (!mShowProtection) {
-                        requestLayout();
-                    }
-                }
-            });
-            mCameraProtectionAnimator.start();
-        }
-
-        private void update() {
-            if (!isAttachedToWindow() || mDecorations.mPendingRotationChange) {
+        @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+        @Override
+        public void updateCutout() {
+            if (!isAttachedToWindow() || pendingRotationChange) {
                 return;
             }
             mPosition = getBoundPositionFromRotation(mInitialPosition, mRotation);
             requestLayout();
-            getDisplay().getDisplayInfo(mInfo);
+            getDisplay().getDisplayInfo(displayInfo);
             mBounds.clear();
             mBoundingRect.setEmpty();
-            mBoundingPath.reset();
+            cutoutPath.reset();
             int newVisible;
             if (shouldDrawCutout(getContext()) && hasCutout()) {
-                mBounds.addAll(mInfo.displayCutout.getBoundingRects());
+                mBounds.addAll(displayInfo.displayCutout.getBoundingRects());
                 localBounds(mBoundingRect);
                 updateGravity();
                 updateBoundingPath();
@@ -1291,10 +1320,11 @@
         }
 
         private void updateBoundingPath() {
-            int lw = mInfo.logicalWidth;
-            int lh = mInfo.logicalHeight;
+            int lw = displayInfo.logicalWidth;
+            int lh = displayInfo.logicalHeight;
 
-            boolean flipped = mInfo.rotation == ROTATION_90 || mInfo.rotation == ROTATION_270;
+            boolean flipped = displayInfo.rotation == ROTATION_90
+                    || displayInfo.rotation == ROTATION_270;
 
             int dw = flipped ? lh : lw;
             int dh = flipped ? lw : lh;
@@ -1302,49 +1332,20 @@
             Path path = DisplayCutout.pathFromResources(
                     getResources(), getDisplay().getUniqueId(), dw, dh);
             if (path != null) {
-                mBoundingPath.set(path);
+                cutoutPath.set(path);
             } else {
-                mBoundingPath.reset();
+                cutoutPath.reset();
             }
             Matrix m = new Matrix();
-            transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
-            mBoundingPath.transform(m);
-            if (mProtectionPathOrig != null) {
-                // Reset the protection path so we don't aggregate rotations
-                mProtectionPath.set(mProtectionPathOrig);
-                mProtectionPath.transform(m);
-                m.mapRect(mProtectionRect, mProtectionRectOrig);
-            }
-        }
-
-        private static void transformPhysicalToLogicalCoordinates(@Surface.Rotation int rotation,
-                @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
-            switch (rotation) {
-                case ROTATION_0:
-                    out.reset();
-                    break;
-                case ROTATION_90:
-                    out.setRotate(270);
-                    out.postTranslate(0, physicalWidth);
-                    break;
-                case ROTATION_180:
-                    out.setRotate(180);
-                    out.postTranslate(physicalWidth, physicalHeight);
-                    break;
-                case ROTATION_270:
-                    out.setRotate(90);
-                    out.postTranslate(physicalHeight, 0);
-                    break;
-                default:
-                    throw new IllegalArgumentException("Unknown rotation: " + rotation);
-            }
+            transformPhysicalToLogicalCoordinates(displayInfo.rotation, dw, dh, m);
+            cutoutPath.transform(m);
         }
 
         private void updateGravity() {
             LayoutParams lp = getLayoutParams();
             if (lp instanceof FrameLayout.LayoutParams) {
                 FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) lp;
-                int newGravity = getGravity(mInfo.displayCutout);
+                int newGravity = getGravity(displayInfo.displayCutout);
                 if (flp.gravity != newGravity) {
                     flp.gravity = newGravity;
                     setLayoutParams(flp);
@@ -1353,7 +1354,7 @@
         }
 
         private boolean hasCutout() {
-            final DisplayCutout displayCutout = mInfo.displayCutout;
+            final DisplayCutout displayCutout = displayInfo.displayCutout;
             if (displayCutout == null) {
                 return false;
             }
@@ -1377,11 +1378,11 @@
                 return;
             }
 
-            if (mShowProtection) {
+            if (showProtection) {
                 // Make sure that our measured height encompases the protection
                 mTotalBounds.union(mBoundingRect);
-                mTotalBounds.union((int) mProtectionRect.left, (int) mProtectionRect.top,
-                        (int) mProtectionRect.right, (int) mProtectionRect.bottom);
+                mTotalBounds.union((int) protectionRect.left, (int) protectionRect.top,
+                        (int) protectionRect.right, (int) protectionRect.bottom);
                 setMeasuredDimension(
                         resolveSizeAndState(mTotalBounds.width(), widthMeasureSpec, 0),
                         resolveSizeAndState(mTotalBounds.height(), heightMeasureSpec, 0));
@@ -1413,7 +1414,7 @@
         }
 
         private void localBounds(Rect out) {
-            DisplayCutout displayCutout = mInfo.displayCutout;
+            DisplayCutout displayCutout = displayInfo.displayCutout;
             boundsFromDirection(displayCutout, getGravity(displayCutout), out);
         }
 
@@ -1437,32 +1438,6 @@
             }
             return Gravity.NO_GRAVITY;
         }
-
-        @Override
-        public boolean shouldInterceptTouch() {
-            return mInfo.displayCutout != null && getVisibility() == VISIBLE;
-        }
-
-        @Override
-        public Region getInterceptRegion() {
-            if (mInfo.displayCutout == null) {
-                return null;
-            }
-
-            View rootView = getRootView();
-            Region cutoutBounds = rectsToRegion(
-                    mInfo.displayCutout.getBoundingRects());
-
-            // Transform to window's coordinate space
-            rootView.getLocationOnScreen(mLocation);
-            cutoutBounds.translate(-mLocation[0], -mLocation[1]);
-
-            // Intersect with window's frame
-            cutoutBounds.op(rootView.getLeft(), rootView.getTop(), rootView.getRight(),
-                    rootView.getBottom(), Region.Op.INTERSECT);
-
-            return cutoutBounds;
-        }
     }
 
     /**
@@ -1473,6 +1448,8 @@
 
         private final View mView;
         private final int mTargetRotation;
+        // Pass -1 for ScreenDecorHwcLayer since it's a fullscreen window and has no specific
+        // position.
         private final int mPosition;
 
         private RestartingPreDrawListener(View view, @BoundsPosition int position,
@@ -1488,7 +1465,9 @@
 
             if (mTargetRotation == mRotation) {
                 if (DEBUG) {
-                    Log.i(TAG, getWindowTitleByPos(mPosition) + " already in target rot "
+                    final String title = mPosition < 0 ? "ScreenDecorHwcLayer"
+                            : getWindowTitleByPos(mPosition);
+                    Log.i(TAG, title + " already in target rot "
                             + mTargetRotation + ", allow draw without restarting it");
                 }
                 return true;
@@ -1499,7 +1478,9 @@
             // take effect.
             updateOrientation();
             if (DEBUG) {
-                Log.i(TAG, getWindowTitleByPos(mPosition)
+                final String title = mPosition < 0 ? "ScreenDecorHwcLayer"
+                        : getWindowTitleByPos(mPosition);
+                Log.i(TAG, title
                         + " restarting listener fired, restarting draw for rot " + mRotation);
             }
             mView.invalidate();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index bbe0a99..89735c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -27,6 +27,7 @@
 import android.provider.DeviceConfig.NAMESPACE_SYSTEMUI
 import android.text.format.DateUtils
 import android.util.ArrayMap
+import android.util.IndentingPrintWriter
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
@@ -38,14 +39,19 @@
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED
+import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.indentIfPossible
 import com.android.systemui.util.time.SystemClock
+import java.io.FileDescriptor
+import java.io.PrintWriter
 import java.util.Objects
 import java.util.concurrent.Executor
 import javax.inject.Inject
@@ -60,8 +66,9 @@
     private val activityManager: IActivityManager,
     private val packageManager: PackageManager,
     private val deviceConfigProxy: DeviceConfigProxy,
-    private val dialogLaunchAnimator: DialogLaunchAnimator
-) : IForegroundServiceObserver.Stub() {
+    private val dialogLaunchAnimator: DialogLaunchAnimator,
+    private val dumpManager: DumpManager
+) : IForegroundServiceObserver.Stub(), Dumpable {
 
     companion object {
         private val LOG_TAG = FgsManagerController::class.java.simpleName
@@ -116,6 +123,8 @@
             isAvailable = deviceConfigProxy
                     .getBoolean(NAMESPACE_SYSTEMUI, TASK_MANAGER_ENABLED, true)
 
+            dumpManager.registerDumpable(this)
+
             initialized = true
         }
     }
@@ -379,6 +388,16 @@
         }
 
         override fun hashCode(): Int = Objects.hash(userId, packageName)
+
+        fun dump(pw: PrintWriter) {
+            pw.println("UserPackage: [")
+            pw.indentIfPossible {
+                pw.println("userId=$userId")
+                pw.println("packageName=$packageName")
+                pw.println("uiControl=$uiControl")
+            }
+            pw.println("]")
+        }
     }
 
     private data class StartTimeAndTokens(
@@ -398,6 +417,22 @@
         fun isEmpty(): Boolean {
             return tokens.isEmpty()
         }
+
+        fun dump(pw: PrintWriter) {
+            pw.println("StartTimeAndTokens: [")
+            pw.indentIfPossible {
+                pw.println("startTime=$startTime (time running =" +
+                        " ${systemClock.elapsedRealtime() - startTime}ms)")
+                pw.println("tokens: [")
+                pw.indentIfPossible {
+                    for (token in tokens) {
+                        pw.println("$token")
+                    }
+                }
+                pw.println("]")
+            }
+            pw.println("]")
+        }
     }
 
     private class AppItemViewHolder(parent: View) : RecyclerView.ViewHolder(parent) {
@@ -429,9 +464,54 @@
         var appLabel: CharSequence = ""
         var icon: Drawable? = null
         var stopped = false
+
+        fun dump(pw: PrintWriter, systemClock: SystemClock) {
+            pw.println("RunningApp: [")
+            pw.indentIfPossible {
+                pw.println("userId=$userId")
+                pw.println("packageName=$packageName")
+                pw.println("timeStarted=$timeStarted (time since start =" +
+                        " ${systemClock.elapsedRealtime() - timeStarted}ms)\"")
+                pw.println("uiControl=$uiControl")
+                pw.println("appLabel=$appLabel")
+                pw.println("icon=$icon")
+                pw.println("stopped=$stopped")
+            }
+            pw.println("]")
+        }
     }
 
     private enum class UIControl {
         NORMAL, HIDE_BUTTON, HIDE_ENTRY
     }
+
+    override fun dump(fd: FileDescriptor, printwriter: PrintWriter, args: Array<out String>) {
+        val pw = IndentingPrintWriter(printwriter)
+        synchronized(lock) {
+            pw.println("changesSinceDialog=$changesSinceDialog")
+            pw.println("Running service tokens: [")
+            pw.indentIfPossible {
+                runningServiceTokens.forEach { (userPackage, startTimeAndTokens) ->
+                    pw.println("{")
+                    pw.indentIfPossible {
+                        userPackage.dump(pw)
+                        startTimeAndTokens.dump(pw)
+                    }
+                    pw.println("}")
+                }
+            }
+            pw.println("]")
+
+            pw.println("Loaded package UI info: [")
+            pw.indentIfPossible {
+                runningApps.forEach { (userPackage, runningApp) ->
+                    pw.println("{")
+                    userPackage.dump(pw)
+                    runningApp.dump(pw, systemClock)
+                    pw.println("}")
+                }
+            }
+            pw.println("]")
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index ea68c40..17f85ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -420,7 +420,6 @@
     public void setBrightnessMirrorController(
             BrightnessMirrorController brightnessMirrorController) {
         mQSPanelController.setBrightnessMirror(brightnessMirrorController);
-        mQuickQSPanelController.setBrightnessMirror(brightnessMirrorController);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt
deleted file mode 100644
index 65889d7..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt
+++ /dev/null
@@ -1,102 +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 com.android.systemui.qs
-
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.settings.brightness.BrightnessController
-import com.android.systemui.settings.brightness.BrightnessSliderController
-import com.android.systemui.settings.brightness.MirroredBrightnessController
-import com.android.systemui.statusbar.policy.BrightnessMirrorController
-import javax.inject.Inject
-
-/**
- * Controls brightness slider in QQS, which is visible only in split shade. It's responsible for
- * showing/hiding it when appropriate and (un)registering listeners
- */
-class QuickQSBrightnessController @VisibleForTesting constructor(
-    private val brightnessControllerFactory: () -> BrightnessController
-) : MirroredBrightnessController {
-
-    @Inject constructor(
-        brightnessControllerFactory: BrightnessController.Factory,
-        brightnessSliderControllerFactory: BrightnessSliderController.Factory,
-        quickQSPanel: QuickQSPanel
-    ) : this(brightnessControllerFactory = {
-            val slider = brightnessSliderControllerFactory.create(quickQSPanel.context,
-                    quickQSPanel)
-            slider.init()
-            quickQSPanel.setBrightnessView(slider.rootView)
-            brightnessControllerFactory.create(slider)
-        })
-
-    private var isListening = false
-    private var brightnessController: BrightnessController? = null
-    private var mirrorController: BrightnessMirrorController? = null
-
-    fun init(shouldUseSplitNotificationShade: Boolean) {
-        refreshVisibility(shouldUseSplitNotificationShade)
-    }
-
-    /**
-     * Starts/Stops listening for brightness changing events.
-     * It's fine to call this function even if slider is not visible (which would be the case for
-     * all small screen devices), it will just do nothing in that case
-     */
-    fun setListening(listening: Boolean) {
-        if (listening) {
-            // controller can be null when slider was never shown
-            if (!isListening && brightnessController != null) {
-                brightnessController?.registerCallbacks()
-                isListening = true
-            }
-        } else {
-            brightnessController?.unregisterCallbacks()
-            isListening = false
-        }
-    }
-
-    fun checkRestrictionAndSetEnabled() {
-        brightnessController?.checkRestrictionAndSetEnabled()
-    }
-
-    fun refreshVisibility(shouldUseSplitNotificationShade: Boolean) {
-        if (shouldUseSplitNotificationShade) {
-            showBrightnessSlider()
-        } else {
-            hideBrightnessSlider()
-        }
-    }
-
-    override fun setMirror(controller: BrightnessMirrorController) {
-        mirrorController = controller
-        mirrorController?.let { brightnessController?.setMirror(it) }
-    }
-
-    private fun hideBrightnessSlider() {
-        brightnessController?.hideSlider()
-    }
-
-    private fun showBrightnessSlider() {
-        if (brightnessController == null) {
-            brightnessController = brightnessControllerFactory()
-            mirrorController?.also { brightnessController?.setMirror(it) }
-            brightnessController?.registerCallbacks()
-            isListening = true
-        }
-        brightnessController?.showSlider()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index a3af0e5..b2e008b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -33,8 +33,6 @@
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
-import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.util.leak.RotationUtils;
 
 import java.util.ArrayList;
@@ -55,10 +53,6 @@
                 }
             };
 
-    // brightness is visible only in split shade
-    private final QuickQSBrightnessController mBrightnessController;
-    private final BrightnessMirrorHandler mBrightnessMirrorHandler;
-
     private final MediaFlags mMediaFlags;
     private final boolean mUsingCollapsedLandscapeMedia;
 
@@ -70,13 +64,10 @@
             @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA) boolean usingCollapsedLandscapeMedia,
             MediaFlags mediaFlags,
             MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
-            DumpManager dumpManager,
-            QuickQSBrightnessController quickQSBrightnessController
+            DumpManager dumpManager
     ) {
         super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
                 uiEventLogger, qsLogger, dumpManager);
-        mBrightnessController = quickQSBrightnessController;
-        mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
         mUsingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia;
         mMediaFlags = mediaFlags;
     }
@@ -87,7 +78,6 @@
         updateMediaExpansion();
         mMediaHost.setShowsOnlyActiveMedia(true);
         mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
-        mBrightnessController.init(mShouldUseSplitNotificationShade);
     }
 
     private void updateMediaExpansion() {
@@ -111,20 +101,17 @@
     protected void onViewAttached() {
         super.onViewAttached();
         mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
-        mBrightnessMirrorHandler.onQsPanelAttached();
     }
 
     @Override
     protected void onViewDetached() {
         super.onViewDetached();
         mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
-        mBrightnessMirrorHandler.onQsPanelDettached();
     }
 
     @Override
     void setListening(boolean listening) {
         super.setListening(listening);
-        mBrightnessController.setListening(listening);
     }
 
     public boolean isListening() {
@@ -137,14 +124,7 @@
     }
 
     @Override
-    public void refreshAllTiles() {
-        mBrightnessController.checkRestrictionAndSetEnabled();
-        super.refreshAllTiles();
-    }
-
-    @Override
     protected void onConfigurationChanged() {
-        mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade);
         updateMediaExpansion();
     }
 
@@ -168,8 +148,4 @@
     public int getNumQuickTiles() {
         return mView.getNumQuickTiles();
     }
-
-    public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) {
-        mBrightnessMirrorHandler.setController(brightnessMirrorController);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 4d9c4c2..e8d27ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -199,7 +199,7 @@
             holder.init(dialog);
             SystemUIDialog.setShowForAllUsers(dialog, true);
             SystemUIDialog.registerDismissListener(dialog);
-            SystemUIDialog.setWindowOnTop(dialog);
+            SystemUIDialog.setWindowOnTop(dialog, mKeyguard.isShowing());
 
             mUiHandler.post(() -> {
                 if (view != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index e116f75..04a25fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -59,6 +59,7 @@
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.connectivity.SignalCallback;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import javax.inject.Inject;
 
@@ -68,7 +69,7 @@
 
     private final NetworkController mController;
     private final DataUsageController mDataController;
-
+    private final KeyguardStateController mKeyguard;
     private final CellSignalCallback mSignalCallback = new CellSignalCallback();
 
     @Inject
@@ -81,11 +82,14 @@
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
-            NetworkController networkController
+            NetworkController networkController,
+            KeyguardStateController keyguardStateController
+
     ) {
         super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
         mController = networkController;
+        mKeyguard = keyguardStateController;
         mDataController = mController.getMobileDataController();
         mController.observe(getLifecycle(), mSignalCallback);
     }
@@ -145,7 +149,7 @@
         dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
         SystemUIDialog.setShowForAllUsers(dialog, true);
         SystemUIDialog.registerDismissListener(dialog);
-        SystemUIDialog.setWindowOnTop(dialog);
+        SystemUIDialog.setWindowOnTop(dialog, mKeyguard.isShowing());
         dialog.show();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 8e01942..8b6ddb4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -65,6 +65,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.wifitrackerlib.WifiEntry;
 
 import java.util.List;
@@ -130,6 +131,7 @@
     private Button mDoneButton;
     private Button mAirplaneModeButton;
     private Drawable mBackgroundOn;
+    private KeyguardStateController mKeyguard;
     @Nullable
     private Drawable mBackgroundOff = null;
     private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -159,7 +161,8 @@
     public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
             InternetDialogController internetDialogController, boolean canConfigMobileData,
             boolean canConfigWifi, boolean aboveStatusBar, UiEventLogger uiEventLogger,
-            @Main Handler handler, @Background Executor executor) {
+            @Main Handler handler, @Background Executor executor,
+            KeyguardStateController keyguardStateController) {
         super(context);
         if (DEBUG) {
             Log.d(TAG, "Init InternetDialog");
@@ -177,6 +180,7 @@
         mWifiManager = mInternetDialogController.getWifiManager();
         mCanConfigMobileData = canConfigMobileData;
         mCanConfigWifi = canConfigWifi;
+        mKeyguard = keyguardStateController;
 
         mUiEventLogger = uiEventLogger;
         mAdapter = new InternetAdapter(mInternetDialogController);
@@ -615,7 +619,7 @@
         mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
         SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
         SystemUIDialog.registerDismissListener(mAlertDialog);
-        SystemUIDialog.setWindowOnTop(mAlertDialog);
+        SystemUIDialog.setWindowOnTop(mAlertDialog, mKeyguard.isShowing());
         mAlertDialog.show();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
index 79f7ac3..4386169 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.policy.KeyguardStateController
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
@@ -40,7 +41,8 @@
     private val internetDialogController: InternetDialogController,
     private val context: Context,
     private val uiEventLogger: UiEventLogger,
-    private val dialogLaunchAnimator: DialogLaunchAnimator
+    private val dialogLaunchAnimator: DialogLaunchAnimator,
+    private val keyguardStateController: KeyguardStateController
 ) {
     companion object {
         var internetDialog: InternetDialog? = null
@@ -61,7 +63,7 @@
         } else {
             internetDialog = InternetDialog(context, this, internetDialogController,
                     canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler,
-                    executor)
+                    executor, keyguardStateController)
             if (view != null) {
                 dialogLaunchAnimator.showFromView(internetDialog!!, view,
                     animateBackgroundBoundsChange = true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index ff9fc30..3dd717d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.phone.NotificationIconContainer.MAX_ICONS_ON_LOCKSCREEN;
-
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -41,6 +39,7 @@
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.NotificationBackgroundView;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
@@ -48,7 +47,6 @@
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
 import com.android.systemui.statusbar.notification.stack.ViewState;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.util.Utils;
 
 /**
  * A notification shelf view that is placed inside the notification scroller. It manages the
@@ -85,7 +83,7 @@
     private int mIndexOfFirstViewInShelf = -1;
     private float mCornerAnimationDistance;
     private NotificationShelfController mController;
-    private int mActualWidth = -1;
+    private float mActualWidth = -1;
 
     /** Fraction of lockscreen to shade animation (on lockscreen swipe down). */
     private float mFractionToShade;
@@ -211,10 +209,6 @@
 
             final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
             viewState.yTranslation = stackEnd - viewState.height;
-
-            final int shortestWidth = mShelfIcons.calculateWidthFor(MAX_ICONS_ON_LOCKSCREEN);
-            final float fraction = Interpolators.STANDARD.getInterpolation(mFractionToShade);
-            updateStateWidth(viewState, fraction, shortestWidth);
         } else {
             viewState.hidden = true;
             viewState.location = ExpandableViewState.LOCATION_GONE;
@@ -223,15 +217,23 @@
     }
 
     /**
-     * @param shelfState View state for NotificationShelf
-     * @param fraction Fraction of lockscreen to shade transition
+     * @param fractionToShade Fraction of lockscreen to shade transition
      * @param shortestWidth Shortest width to use for lockscreen shelf
      */
     @VisibleForTesting
-    public void updateStateWidth(ShelfState shelfState, float fraction, int shortestWidth) {
-        shelfState.actualWidth = mAmbientState.isOnKeyguard()
-                ? (int) MathUtils.lerp(shortestWidth, getWidth(), fraction)
+    public void updateActualWidth(float fractionToShade, float shortestWidth) {
+        final float actualWidth = mAmbientState.isOnKeyguard()
+                ? MathUtils.lerp(shortestWidth, getWidth(), fractionToShade)
                 : getWidth();
+        ActivatableNotificationView anv = (ActivatableNotificationView) this;
+        NotificationBackgroundView bg = anv.getBackgroundNormal();
+        if (bg != null) {
+            anv.getBackgroundNormal().setActualWidth((int) actualWidth);
+        }
+        if (mShelfIcons != null) {
+            mShelfIcons.setActualLayoutWidth((int) actualWidth);
+        }
+        mActualWidth = actualWidth;
     }
 
     /**
@@ -245,7 +247,7 @@
      * @return Actual width of shelf, accounting for possible ongoing width animation
      */
     public int getActualWidth() {
-        return mActualWidth > -1 ? mActualWidth : getWidth();
+        return mActualWidth > -1 ? (int) mActualWidth : getWidth();
     }
 
     /**
@@ -412,6 +414,10 @@
                 || !mShowNotificationShelf
                 || numViewsInShelf < 1f;
 
+        final float fractionToShade = Interpolators.STANDARD.getInterpolation(mFractionToShade);
+        final float shortestWidth = mShelfIcons.calculateWidthFor(numViewsInShelf);
+        updateActualWidth(fractionToShade, shortestWidth);
+
         // TODO(b/172289889) transition last icon in shelf to notification icon and vice versa.
         setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
         setBackgroundTop(backgroundTop);
@@ -921,30 +927,17 @@
     public class ShelfState extends ExpandableViewState {
         private boolean hasItemsInStableShelf;
         private ExpandableView firstViewInShelf;
-        public int actualWidth = -1;
-
-        private void updateShelfWidth(View view) {
-            if (actualWidth < 0) {
-                return;
-            }
-            mActualWidth = actualWidth;
-            ActivatableNotificationView anv = (ActivatableNotificationView) view;
-            anv.getBackgroundNormal().setActualWidth(actualWidth);
-            mShelfIcons.setActualLayoutWidth(actualWidth);
-        }
 
         @Override
         public void applyToView(View view) {
             if (!mShowNotificationShelf) {
                 return;
             }
-
             super.applyToView(view);
             setIndexOfFirstViewInShelf(firstViewInShelf);
             updateAppearance();
             setHasItemsInStableShelf(hasItemsInStableShelf);
             mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
-            updateShelfWidth(view);
         }
 
         @Override
@@ -952,13 +945,11 @@
             if (!mShowNotificationShelf) {
                 return;
             }
-
             super.animateTo(view, properties);
             setIndexOfFirstViewInShelf(firstViewInShelf);
             updateAppearance();
             setHasItemsInStableShelf(hasItemsInStableShelf);
             mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
-            updateShelfWidth(view);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 962c7fa..1401423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -92,6 +92,8 @@
     private val views: Sequence<View>
         get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
 
+    private var showingListener: ShowingListener? = null
+
     init {
         contentInsetsProvider.addCallback(object : StatusBarContentInsetsChangedListener {
             override fun onStatusBarContentInsetsChanged() {
@@ -132,6 +134,10 @@
         uiExecutor = e
     }
 
+    fun setShowingListener(l: ShowingListener) {
+        showingListener = l
+    }
+
     fun setQsExpanded(expanded: Boolean) {
         dlog("setQsExpanded $expanded")
         synchronized(lock) {
@@ -176,15 +182,20 @@
                     .setDuration(DURATION)
                     .setInterpolator(Interpolators.ALPHA_OUT)
                     .alpha(0f)
-                    .withEndAction { dot.visibility = View.INVISIBLE }
+                    .withEndAction {
+                        dot.visibility = View.INVISIBLE
+                        showingListener?.onPrivacyDotHidden(dot)
+                    }
                     .start()
         } else {
             dot.visibility = View.INVISIBLE
+            showingListener?.onPrivacyDotHidden(dot)
         }
     }
 
     @UiThread
     private fun showDotView(dot: View, animate: Boolean) {
+        showingListener?.onPrivacyDotShown(dot)
         dot.clearAnimation()
         if (animate) {
             dot.visibility = View.VISIBLE
@@ -320,6 +331,7 @@
     @UiThread
     private fun updateDesignatedCorner(newCorner: View?, shouldShowDot: Boolean) {
         if (shouldShowDot) {
+            showingListener?.onPrivacyDotShown(newCorner)
             newCorner?.apply {
                 clearAnimation()
                 visibility = View.VISIBLE
@@ -336,6 +348,11 @@
     private fun setCornerVisibilities(vis: Int) {
         views.forEach { corner ->
             corner.visibility = vis
+            if (vis == View.VISIBLE) {
+                showingListener?.onPrivacyDotShown(corner)
+            } else {
+                showingListener?.onPrivacyDotHidden(corner)
+            }
         }
     }
 
@@ -555,6 +572,11 @@
             )
         }
     }
+
+    public interface ShowingListener {
+        fun onPrivacyDotShown(v: View?)
+        fun onPrivacyDotHidden(v: View?)
+    }
 }
 
 private fun dlog(s: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index ebfed1a..034b751 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -387,16 +387,18 @@
     }
 
     /**
-     * @return Width of shelf for the given number of icons and overflow dot
+     * @return Width of shelf for the given number of icons
      */
-    public int calculateWidthFor(int numMaxIcons) {
+    public float calculateWidthFor(float numIcons) {
         if (getChildCount() == 0) {
-            return 0;
+            return 0f;
         }
-        return (int) (getActualPaddingStart()
-                + numMaxIcons * mIconSize
-                + mOverflowWidth
-                + getActualPaddingEnd());
+        final float contentWidth = numIcons <= MAX_ICONS_ON_LOCKSCREEN + 1
+                ? numIcons * mIconSize
+                : MAX_ICONS_ON_LOCKSCREEN * mIconSize + (float) mOverflowWidth;
+        return getActualPaddingStart()
+                + contentWidth
+                + getActualPaddingEnd();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index d6fc0a4..6e1ec9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -43,7 +43,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 /**
  * Base class for dialogs that should appear over panels and keyguard.
@@ -220,10 +219,13 @@
         }
     }
 
-    public static void setWindowOnTop(Dialog dialog) {
+    /**
+     * Ensure the window type is set properly to show over all other screens
+     */
+    public static void setWindowOnTop(Dialog dialog, boolean isKeyguardShowing) {
         final Window window = dialog.getWindow();
         window.setType(LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
-        if (Dependency.get(KeyguardStateController.class).isShowing()) {
+        if (isKeyguardShowing) {
             window.getAttributes().setFitInsetsTypes(
                     window.getAttributes().getFitInsetsTypes() & ~Type.statusBars());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index e416ed1..f4e53e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -25,7 +25,6 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.IActivityManager;
-import android.app.IActivityTaskManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -39,7 +38,6 @@
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -78,11 +76,14 @@
 import com.android.systemui.qs.QSUserSwitcherEvent;
 import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.user.CreateUserActivity;
 import com.android.systemui.util.settings.SecureSettings;
 
+import dagger.Lazy;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -125,10 +126,10 @@
     private final ActivityStarter mActivityStarter;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final TelephonyListenerManager mTelephonyListenerManager;
-    private final IActivityTaskManager mActivityTaskManager;
     private final InteractionJankMonitor mInteractionJankMonitor;
     private final LatencyTracker mLatencyTracker;
     private final DialogLaunchAnimator mDialogLaunchAnimator;
+    private final Lazy<ShadeController> mShadeController;
 
     private ArrayList<UserRecord> mUsers = new ArrayList<>();
     @VisibleForTesting
@@ -149,6 +150,7 @@
     private final UiEventLogger mUiEventLogger;
     private final IActivityManager mActivityManager;
     private final Executor mBgExecutor;
+    private final Executor mUiExecutor;
     private final boolean mGuestUserAutoCreated;
     private final AtomicBoolean mGuestIsResetting;
     private final AtomicBoolean mGuestCreationScheduled;
@@ -170,19 +172,19 @@
             UiEventLogger uiEventLogger,
             FalsingManager falsingManager,
             TelephonyListenerManager telephonyListenerManager,
-            IActivityTaskManager activityTaskManager,
             SecureSettings secureSettings,
             @Background Executor bgExecutor,
+            @Main Executor uiExecutor,
             InteractionJankMonitor interactionJankMonitor,
             LatencyTracker latencyTracker,
             DumpManager dumpManager,
+            Lazy<ShadeController> shadeController,
             DialogLaunchAnimator dialogLaunchAnimator) {
         mContext = context;
         mActivityManager = activityManager;
         mUserTracker = userTracker;
         mBroadcastDispatcher = broadcastDispatcher;
         mTelephonyListenerManager = telephonyListenerManager;
-        mActivityTaskManager = activityTaskManager;
         mUiEventLogger = uiEventLogger;
         mFalsingManager = falsingManager;
         mInteractionJankMonitor = interactionJankMonitor;
@@ -190,6 +192,7 @@
         mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
                 this, mUserTracker, mUiEventLogger, secureSettings);
         mBgExecutor = bgExecutor;
+        mUiExecutor = uiExecutor;
         if (!UserManager.isGuestUserEphemeral()) {
             mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
         }
@@ -204,6 +207,7 @@
         mActivityStarter = activityStarter;
         mUserManager = userManager;
         mDialogLaunchAnimator = dialogLaunchAnimator;
+        mShadeController = shadeController;
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_ADDED);
@@ -289,109 +293,100 @@
         mForcePictureLoadForUserId.clear();
 
         final boolean addUsersWhenLocked = mAddUsersFromLockScreen;
-        new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
-                final SparseArray<Bitmap> bitmaps = params[0];
-                List<UserInfo> infos = mUserManager.getAliveUsers();
-                if (infos == null) {
-                    return null;
-                }
-                ArrayList<UserRecord> records = new ArrayList<>(infos.size());
-                int currentId = mUserTracker.getUserId();
-                // Check user switchability of the foreground user since SystemUI is running in
-                // User 0
-                boolean canSwitchUsers = mUserManager.getUserSwitchability(
-                        UserHandle.of(mUserTracker.getUserId())) == SWITCHABILITY_STATUS_OK;
-                UserRecord guestRecord = null;
+        mBgExecutor.execute(() ->  {
+            List<UserInfo> infos = mUserManager.getAliveUsers();
+            if (infos == null) {
+                return;
+            }
+            ArrayList<UserRecord> records = new ArrayList<>(infos.size());
+            int currentId = mUserTracker.getUserId();
+            // Check user switchability of the foreground user since SystemUI is running in
+            // User 0
+            boolean canSwitchUsers = mUserManager.getUserSwitchability(
+                    UserHandle.of(mUserTracker.getUserId())) == SWITCHABILITY_STATUS_OK;
+            UserRecord guestRecord = null;
 
-                for (UserInfo info : infos) {
-                    boolean isCurrent = currentId == info.id;
-                    boolean switchToEnabled = canSwitchUsers || isCurrent;
-                    if (info.isEnabled()) {
-                        if (info.isGuest()) {
-                            // Tapping guest icon triggers remove and a user switch therefore
-                            // the icon shouldn't be enabled even if the user is current
-                            guestRecord = new UserRecord(info, null /* picture */,
-                                    true /* isGuest */, isCurrent, false /* isAddUser */,
-                                    false /* isRestricted */, canSwitchUsers,
-                                    false /* isAddSupervisedUser */);
-                        } else if (info.supportsSwitchToByUser()) {
-                            Bitmap picture = bitmaps.get(info.id);
-                            if (picture == null) {
-                                picture = mUserManager.getUserIcon(info.id);
-
-                                if (picture != null) {
-                                    int avatarSize = mContext.getResources()
-                                            .getDimensionPixelSize(R.dimen.max_avatar_size);
-                                    picture = Bitmap.createScaledBitmap(
-                                            picture, avatarSize, avatarSize, true);
-                                }
-                            }
-                            records.add(new UserRecord(info, picture, false /* isGuest */,
-                                    isCurrent, false /* isAddUser */, false /* isRestricted */,
-                                    switchToEnabled, false /* isAddSupervisedUser */));
-                        }
-                    }
-                }
-                if (records.size() > 1 || guestRecord != null) {
-                    Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true);
-                }
-
-                if (guestRecord == null) {
-                    if (mGuestUserAutoCreated) {
-                        // If mGuestIsResetting=true, the switch should be disabled since
-                        // we will just use it as an indicator for "Resetting guest...".
-                        // Otherwise, default to canSwitchUsers.
-                        boolean isSwitchToGuestEnabled =
-                                !mGuestIsResetting.get() && canSwitchUsers;
-                        guestRecord = new UserRecord(null /* info */, null /* picture */,
-                                true /* isGuest */, false /* isCurrent */,
-                                false /* isAddUser */, false /* isRestricted */,
-                                isSwitchToGuestEnabled, false /* isAddSupervisedUser */);
-                        checkIfAddUserDisallowedByAdminOnly(guestRecord);
-                        records.add(guestRecord);
-                    } else if (canCreateGuest(guestRecord != null)) {
-                        guestRecord = new UserRecord(null /* info */, null /* picture */,
-                                true /* isGuest */, false /* isCurrent */,
-                                false /* isAddUser */, createIsRestricted(), canSwitchUsers,
+            for (UserInfo info : infos) {
+                boolean isCurrent = currentId == info.id;
+                boolean switchToEnabled = canSwitchUsers || isCurrent;
+                if (info.isEnabled()) {
+                    if (info.isGuest()) {
+                        // Tapping guest icon triggers remove and a user switch therefore
+                        // the icon shouldn't be enabled even if the user is current
+                        guestRecord = new UserRecord(info, null /* picture */,
+                                true /* isGuest */, isCurrent, false /* isAddUser */,
+                                false /* isRestricted */, canSwitchUsers,
                                 false /* isAddSupervisedUser */);
-                        checkIfAddUserDisallowedByAdminOnly(guestRecord);
-                        records.add(guestRecord);
+                    } else if (info.supportsSwitchToByUser()) {
+                        Bitmap picture = bitmaps.get(info.id);
+                        if (picture == null) {
+                            picture = mUserManager.getUserIcon(info.id);
+
+                            if (picture != null) {
+                                int avatarSize = mContext.getResources()
+                                        .getDimensionPixelSize(R.dimen.max_avatar_size);
+                                picture = Bitmap.createScaledBitmap(
+                                        picture, avatarSize, avatarSize, true);
+                            }
+                        }
+                        records.add(new UserRecord(info, picture, false /* isGuest */,
+                                isCurrent, false /* isAddUser */, false /* isRestricted */,
+                                switchToEnabled, false /* isAddSupervisedUser */));
                     }
-                } else {
+                }
+            }
+            if (records.size() > 1 || guestRecord != null) {
+                Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true);
+            }
+
+            if (guestRecord == null) {
+                if (mGuestUserAutoCreated) {
+                    // If mGuestIsResetting=true, the switch should be disabled since
+                    // we will just use it as an indicator for "Resetting guest...".
+                    // Otherwise, default to canSwitchUsers.
+                    boolean isSwitchToGuestEnabled = !mGuestIsResetting.get() && canSwitchUsers;
+                    guestRecord = new UserRecord(null /* info */, null /* picture */,
+                            true /* isGuest */, false /* isCurrent */,
+                            false /* isAddUser */, false /* isRestricted */,
+                            isSwitchToGuestEnabled, false /* isAddSupervisedUser */);
+                    checkIfAddUserDisallowedByAdminOnly(guestRecord);
+                    records.add(guestRecord);
+                } else if (canCreateGuest(guestRecord != null)) {
+                    guestRecord = new UserRecord(null /* info */, null /* picture */,
+                            true /* isGuest */, false /* isCurrent */,
+                            false /* isAddUser */, createIsRestricted(), canSwitchUsers,
+                            false /* isAddSupervisedUser */);
+                    checkIfAddUserDisallowedByAdminOnly(guestRecord);
                     records.add(guestRecord);
                 }
-
-                if (canCreateUser()) {
-                    UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
-                            false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
-                            createIsRestricted(), canSwitchUsers,
-                            false /* isAddSupervisedUser */);
-                    checkIfAddUserDisallowedByAdminOnly(addUserRecord);
-                    records.add(addUserRecord);
-                }
-
-                if (canCreateSupervisedUser()) {
-                    UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
-                            false /* isGuest */, false /* isCurrent */, false /* isAddUser */,
-                            createIsRestricted(), canSwitchUsers, true /* isAddSupervisedUser */);
-                    checkIfAddUserDisallowedByAdminOnly(addUserRecord);
-                    records.add(addUserRecord);
-                }
-
-                return records;
+            } else {
+                records.add(guestRecord);
             }
 
-            @Override
-            protected void onPostExecute(ArrayList<UserRecord> userRecords) {
-                if (userRecords != null) {
-                    mUsers = userRecords;
+            if (canCreateUser()) {
+                UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
+                        false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
+                        createIsRestricted(), canSwitchUsers,
+                        false /* isAddSupervisedUser */);
+                checkIfAddUserDisallowedByAdminOnly(addUserRecord);
+                records.add(addUserRecord);
+            }
+
+            if (canCreateSupervisedUser()) {
+                UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
+                        false /* isGuest */, false /* isCurrent */, false /* isAddUser */,
+                        createIsRestricted(), canSwitchUsers, true /* isAddSupervisedUser */);
+                checkIfAddUserDisallowedByAdminOnly(addUserRecord);
+                records.add(addUserRecord);
+            }
+
+            mUiExecutor.execute(() -> {
+                if (records != null) {
+                    mUsers = records;
                     notifyAdapters();
                 }
-            }
-        }.execute((SparseArray) bitmaps);
+            });
+        });
     }
 
     boolean systemCanCreateUsers() {
@@ -1159,7 +1154,7 @@
                     context.getString(mGuestUserAutoCreated
                             ? com.android.settingslib.R.string.guest_reset_guest_confirm_button
                             : R.string.guest_exit_guest_dialog_remove), this);
-            SystemUIDialog.setWindowOnTop(this);
+            SystemUIDialog.setWindowOnTop(this, mKeyguardStateController.isShowing());
             setCanceledOnTouchOutside(false);
             mGuestId = guestId;
             mTargetId = targetId;
@@ -1194,7 +1189,7 @@
                     context.getString(android.R.string.cancel), this);
             setButton(DialogInterface.BUTTON_POSITIVE,
                     context.getString(android.R.string.ok), this);
-            SystemUIDialog.setWindowOnTop(this);
+            SystemUIDialog.setWindowOnTop(this, mKeyguardStateController.isShowing());
         }
 
         @Override
@@ -1211,33 +1206,8 @@
                 if (ActivityManager.isUserAMonkey()) {
                     return;
                 }
-                Intent intent = CreateUserActivity.createIntentForStart(getContext());
-
-                // There are some differences between ActivityStarter and ActivityTaskManager in
-                // terms of how they start an activity. ActivityStarter hides the notification bar
-                // before starting the activity to make sure nothing is in front of the new
-                // activity. ActivityStarter also tries to unlock the device if it's locked.
-                // When locked with PIN/pattern/password then it shows the prompt, if there are no
-                // security steps then it dismisses the keyguard and then starts the activity.
-                // ActivityTaskManager doesn't hide the notification bar or unlocks the device, but
-                // it can start an activity on top of the locked screen.
-                if (!mKeyguardStateController.isUnlocked()
-                        && !mKeyguardStateController.canDismissLockScreen()) {
-                    // Device is locked and can't be unlocked without a PIN/pattern/password so we
-                    // need to use ActivityTaskManager to start the activity on top of the locked
-                    // screen.
-                    try {
-                        mActivityTaskManager.startActivity(null,
-                                mContext.getBasePackageName(), mContext.getAttributionTag(), intent,
-                                intent.resolveTypeIfNeeded(mContext.getContentResolver()), null,
-                                null, 0, 0, null, null);
-                    } catch (RemoteException e) {
-                        e.printStackTrace();
-                        Log.e(TAG, "Couldn't start create user activity", e);
-                    }
-                } else {
-                    mActivityStarter.startActivity(intent, true);
-                }
+                mShadeController.get().collapsePanel();
+                getContext().startActivity(CreateUserActivity.createIntentForStart(getContext()));
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
index 890ee5f..c9de966 100644
--- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -44,7 +44,9 @@
      * Creates an intent to start this activity.
      */
     public static Intent createIntentForStart(Context context) {
-        return new Intent(context, CreateUserActivity.class);
+        Intent intent = new Intent(context, CreateUserActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+        return intent;
     }
 
     private static final String TAG = "CreateUserActivity";
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index 41da44a..14585fb 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -44,6 +44,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
+import com.android.systemui.statusbar.phone.ShadeController
 import com.android.systemui.statusbar.policy.UserSwitcherController
 import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter
 import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord
@@ -63,7 +64,8 @@
     private val broadcastDispatcher: BroadcastDispatcher,
     private val layoutInflater: LayoutInflater,
     private val falsingManager: FalsingManager,
-    private val userManager: UserManager
+    private val userManager: UserManager,
+    private val shadeController: ShadeController
 ) : LifecycleActivity() {
 
     private lateinit var parent: ViewGroup
@@ -250,7 +252,9 @@
                     dismiss()
                     popupMenu = null
 
-                    this@UserSwitcherActivity.finish()
+                    if (!item.isAddUser) {
+                        this@UserSwitcherActivity.finish()
+                    }
             }
 
             show()
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index 7f3d54d..d3c6e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -145,6 +145,10 @@
     }
 
     private void addCallbackLocked(@NotNull Callback callback) {
+        if (mCallbacks.contains(callback)) {
+            return;
+        }
+
         if (shouldLog()) Log.d(mTag, "adding callback");
         mCallbacks.add(callback);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
new file mode 100644
index 0000000..e62b4e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import android.graphics.Canvas
+import android.graphics.Insets
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.Region
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.internal.R
+import com.android.systemui.util.mockito.eq
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class DisplayCutoutBaseViewTest : SysuiTestCase() {
+
+    @Mock private lateinit var mockCanvas: Canvas
+    @Mock private lateinit var mockRootView: View
+    @Mock private lateinit var mockDisplay: Display
+
+    private lateinit var cutoutBaseView: DisplayCutoutBaseView
+    private val cutout: DisplayCutout = DisplayCutout.Builder()
+            .setSafeInsets(Insets.of(0, 2, 0, 0))
+            .setBoundingRectTop(Rect(1, 0, 2, 2))
+            .build()
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun testBoundingRectsToRegion() {
+        setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */)
+        val rect = Rect(1, 2, 3, 4)
+        assertThat(cutoutBaseView.rectsToRegion(listOf(rect)).bounds).isEqualTo(rect)
+    }
+
+    @Test
+    fun testDrawCutout_fillCutout() {
+        setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */)
+        cutoutBaseView.onDraw(mockCanvas)
+
+        verify(cutoutBaseView).drawCutouts(mockCanvas)
+    }
+
+    @Test
+    fun testDrawCutout_notFillCutout() {
+        setupDisplayCutoutBaseView(false /* fillCutout */, true /* hasCutout */)
+        cutoutBaseView.onDraw(mockCanvas)
+
+        verify(cutoutBaseView, never()).drawCutouts(mockCanvas)
+    }
+
+    @Test
+    fun testShouldInterceptTouch_hasCutout() {
+        setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */)
+        cutoutBaseView.updateCutout()
+
+        assertThat(cutoutBaseView.shouldInterceptTouch()).isTrue()
+    }
+
+    @Test
+    fun testShouldInterceptTouch_noCutout() {
+        setupDisplayCutoutBaseView(true /* fillCutout */, false /* hasCutout */)
+        cutoutBaseView.updateCutout()
+
+        assertThat(cutoutBaseView.shouldInterceptTouch()).isFalse()
+    }
+
+    @Test
+    fun testGetInterceptRegion_hasCutout() {
+        setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */)
+        whenever(mockRootView.left).thenReturn(0)
+        whenever(mockRootView.top).thenReturn(0)
+        whenever(mockRootView.right).thenReturn(100)
+        whenever(mockRootView.bottom).thenReturn(200)
+
+        val expect = Region()
+        expect.op(cutout.boundingRectTop, Region.Op.UNION)
+        expect.op(0, 0, 100, 200, Region.Op.INTERSECT)
+
+        cutoutBaseView.updateCutout()
+
+        assertThat(cutoutBaseView.interceptRegion).isEqualTo(expect)
+    }
+
+    @Test
+    fun testGetInterceptRegion_noCutout() {
+        setupDisplayCutoutBaseView(true /* fillCutout */, false /* hasCutout */)
+        cutoutBaseView.updateCutout()
+
+        assertThat(cutoutBaseView.interceptRegion).isNull()
+    }
+
+    @Test
+    fun testCutoutProtection() {
+        setupDisplayCutoutBaseView(true /* fillCutout */, false /* hasCutout */)
+        val bounds = Rect(0, 0, 10, 10)
+        val path = Path()
+        val pathBounds = RectF(bounds)
+        path.addRect(pathBounds, Path.Direction.CCW)
+
+        context.mainExecutor.execute {
+            cutoutBaseView.setProtection(path, bounds)
+            cutoutBaseView.enableShowProtection(true)
+        }
+        waitForIdleSync()
+
+        assertThat(cutoutBaseView.protectionPath.isRect(pathBounds)).isTrue()
+        assertThat(cutoutBaseView.protectionRect).isEqualTo(pathBounds)
+    }
+
+    private fun setupDisplayCutoutBaseView(fillCutout: Boolean, hasCutout: Boolean) {
+        mContext.orCreateTestableResources.addOverride(
+                R.array.config_displayUniqueIdArray, arrayOf<String>())
+        mContext.orCreateTestableResources.addOverride(
+                R.bool.config_fillMainBuiltInDisplayCutout, fillCutout)
+
+        cutoutBaseView = spy(DisplayCutoutBaseView(mContext))
+        whenever(cutoutBaseView.display).thenReturn(mockDisplay)
+        whenever(cutoutBaseView.rootView).thenReturn(mockRootView)
+        whenever(mockDisplay.getDisplayInfo(eq(cutoutBaseView.displayInfo))
+        ).then {
+            val info = it.getArgument<DisplayInfo>(0)
+            info.displayCutout = if (hasCutout) cutout else null
+            return@then true
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 72d72c8..70f3251 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -14,7 +14,6 @@
 
 package com.android.systemui;
 
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
 import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
 import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
@@ -22,7 +21,7 @@
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 
-import static com.android.systemui.ScreenDecorations.rectsToRegion;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -32,7 +31,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.atLeastOnce;
@@ -49,10 +47,12 @@
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Insets;
+import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.VectorDrawable;
 import android.hardware.display.DisplayManager;
+import android.hardware.graphics.common.DisplayDecorationSupport;
 import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -89,7 +89,6 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
-import java.util.Collections;
 
 @RunWithLooper
 @RunWith(AndroidTestingRunner.class)
@@ -106,6 +105,8 @@
     private FakeThreadFactory mThreadFactory;
     private ArrayList<DecorProvider> mDecorProviders;
     @Mock
+    private Display mDisplay;
+    @Mock
     private TunerService mTunerService;
     @Mock
     private BroadcastDispatcher mBroadcastDispatcher;
@@ -140,14 +141,15 @@
                 .getMaximumWindowMetrics();
         when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
         mContext.addMockSystemService(WindowManager.class, mWindowManager);
-
         mDisplayManager = mock(DisplayManager.class);
-        Display display = mContext.getSystemService(DisplayManager.class)
-                .getDisplay(DEFAULT_DISPLAY);
-        when(mDisplayManager.getDisplay(anyInt())).thenReturn(display);
         mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
-        when(mMockTypedArray.length()).thenReturn(0);
 
+        spyOn(mContext);
+        when(mContext.getDisplay()).thenReturn(mDisplay);
+        // Not support hwc layer by default
+        doReturn(null).when(mDisplay).getDisplayDecorationSupport();
+
+        when(mMockTypedArray.length()).thenReturn(0);
         mPrivacyDotTopLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
                 R.id.privacy_dot_top_left_container,
                 DisplayCutout.BOUNDS_POSITION_TOP,
@@ -975,12 +977,6 @@
     }
 
     @Test
-    public void testBoundingRectsToRegion() throws Exception {
-        Rect rect = new Rect(1, 2, 3, 4);
-        assertThat(rectsToRegion(Collections.singletonList(rect)).getBounds(), is(rect));
-    }
-
-    @Test
     public void testRegistration_From_NoOverlay_To_HasOverlays() {
         doReturn(false).when(mScreenDecorations).hasOverlays();
         mScreenDecorations.start();
@@ -1029,6 +1025,114 @@
         assertThat(mScreenDecorations.mIsRegistered, is(false));
     }
 
+    @Test
+    public void testSupportHwcLayer_SwitchFrom_NotSupport() {
+        setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
+                0 /* roundedPadding */, false /* multipleRadius */,
+                true /* fillCutout */, false /* privacyDot */);
+
+        // top cutout
+        final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+        doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+                .when(mScreenDecorations).getCutout();
+
+        mScreenDecorations.start();
+        // should only inflate mOverlays when the hwc doesn't support screen decoration
+        assertNull(mScreenDecorations.mScreenDecorHwcWindow);
+        assertNotNull(mScreenDecorations.mOverlays);
+        assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
+        assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
+
+        final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
+        decorationSupport.format = PixelFormat.R_8;
+        doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
+        // Trigger the support hwc screen decoration change by changing the display unique id
+        mScreenDecorations.mDisplayUniqueId = "test";
+        mScreenDecorations.mDisplayListener.onDisplayChanged(1);
+
+        // should only inflate hwc layer when the hwc supports screen decoration
+        assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
+        assertNull(mScreenDecorations.mOverlays);
+    }
+
+    @Test
+    public void testNotSupportHwcLayer_SwitchFrom_Support() {
+        setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
+                0 /* roundedPadding */, false /* multipleRadius */,
+                true /* fillCutout */, false /* privacyDot */);
+        final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
+        decorationSupport.format = PixelFormat.R_8;
+        doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
+
+        // top cutout
+        final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+        doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+                .when(mScreenDecorations).getCutout();
+
+        mScreenDecorations.start();
+        // should only inflate hwc layer when the hwc supports screen decoration
+        assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
+        assertNull(mScreenDecorations.mOverlays);
+
+        doReturn(null).when(mDisplay).getDisplayDecorationSupport();
+        // Trigger the support hwc screen decoration change by changing the display unique id
+        mScreenDecorations.mDisplayUniqueId = "test";
+        mScreenDecorations.mDisplayListener.onDisplayChanged(1);
+
+        // should only inflate mOverlays when the hwc doesn't support screen decoration
+        assertNull(mScreenDecorations.mScreenDecorHwcWindow);
+        assertNotNull(mScreenDecorations.mOverlays);
+        assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
+        assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
+    }
+
+    @Test
+    public void testHwcLayer_noPrivacyDot() {
+        setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
+                0 /* roundedPadding */, false /* multipleRadius */,
+                true /* fillCutout */, false /* privacyDot */);
+        final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
+        decorationSupport.format = PixelFormat.R_8;
+        doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
+
+        // top cutout
+        final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+        doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+                .when(mScreenDecorations).getCutout();
+
+        mScreenDecorations.start();
+
+        // Should only inflate hwc layer.
+        assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
+        assertNull(mScreenDecorations.mOverlays);
+    }
+
+    @Test
+    public void testHwcLayer_PrivacyDot() {
+        setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
+                0 /* roundedPadding */, false /* multipleRadius */,
+                true /* fillCutout */, true /* privacyDot */);
+        final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
+        decorationSupport.format = PixelFormat.R_8;
+        doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
+
+        // top cutout
+        final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+        doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+                .when(mScreenDecorations).getCutout();
+
+        mScreenDecorations.start();
+
+        assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
+        // mOverlays are inflated but the visibility should be GONE.
+        assertNotNull(mScreenDecorations.mOverlays);
+        final View topOverlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView();
+        final View botOverlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView();
+        assertEquals(topOverlay.getVisibility(), View.INVISIBLE);
+        assertEquals(botOverlay.getVisibility(), View.INVISIBLE);
+
+    }
+
     private void setupResources(int radius, int radiusTop, int radiusBottom, int roundedPadding,
             boolean multipleRadius, boolean fillCutout, boolean privacyDot) {
         mContext.getOrCreateTestableResources().addOverride(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt
deleted file mode 100644
index de1d86b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt
+++ /dev/null
@@ -1,118 +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 com.android.systemui.qs
-
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.settings.brightness.BrightnessController
-import com.android.systemui.statusbar.policy.BrightnessMirrorController
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.mockito.Mock
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.never
-import org.mockito.Mockito.mock
-import org.mockito.junit.MockitoJUnit
-
-@SmallTest
-class QuickQSBrightnessControllerTest : SysuiTestCase() {
-
-    @Mock
-    lateinit var brightnessController: BrightnessController
-    @get:Rule
-    val mockito = MockitoJUnit.rule()
-
-    lateinit var quickQSBrightnessController: QuickQSBrightnessController
-
-    @Before
-    fun setUp() {
-        quickQSBrightnessController = QuickQSBrightnessController(
-                brightnessControllerFactory = { brightnessController })
-    }
-
-    @Test
-    fun testSliderIsShownWhenInitializedInSplitShade() {
-        quickQSBrightnessController.init(shouldUseSplitNotificationShade = true)
-
-        verify(brightnessController).showSlider()
-    }
-
-    @Test
-    fun testSliderIsShownWhenRefreshedInSplitShade() {
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
-
-        verify(brightnessController, times(1)).showSlider()
-    }
-
-    @Test
-    fun testSliderIsHiddenWhenRefreshedInNonSplitShade() {
-        // needs to be shown first
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
-
-        verify(brightnessController).hideSlider()
-    }
-
-    @Test
-    fun testSliderChangesVisibilityWhenRotating() {
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
-        verify(brightnessController, times(1)).showSlider()
-
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
-        verify(brightnessController, times(1)).hideSlider()
-    }
-
-    @Test
-    fun testCallbacksAreRegisteredOnlyOnce() {
-        // this flow simulates expanding shade in portrait...
-        quickQSBrightnessController.setListening(true)
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
-        // ... and rotating to landscape/split shade where slider is visible
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
-
-        verify(brightnessController, times(1)).registerCallbacks()
-    }
-
-    @Test
-    fun testCallbacksAreRegisteredOnlyOnceWhenRotatingPhone() {
-        quickQSBrightnessController.setListening(true)
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
-
-        verify(brightnessController, times(1)).registerCallbacks()
-    }
-
-    @Test
-    fun testCallbacksAreNotRegisteredWhenSliderNotVisible() {
-        quickQSBrightnessController.setListening(true)
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
-
-        verify(brightnessController, never()).registerCallbacks()
-    }
-
-    @Test
-    fun testMirrorIsSetWhenSliderIsShown() {
-        val mirrorController = mock(BrightnessMirrorController::class.java)
-        quickQSBrightnessController.setMirror(mirrorController)
-        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
-
-        verify(brightnessController).setMirror(mirrorController)
-    }
-}
\ No newline at end of file
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 1eb16fd..62915b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -71,8 +71,6 @@
     private lateinit var tileLayout: TileLayout
     @Mock
     private lateinit var tileView: QSTileView
-    @Mock
-    private lateinit var quickQsBrightnessController: QuickQSBrightnessController
     @Captor
     private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
 
@@ -100,8 +98,7 @@
                 metricsLogger,
                 uiEventLogger,
                 qsLogger,
-                dumpManager,
-                quickQsBrightnessController
+                dumpManager
         )
 
         controller.init()
@@ -133,16 +130,6 @@
     }
 
     @Test
-    fun testBrightnessRefreshedWhenConfigurationChanged() {
-        // times(2) because both controller and base controller are registering their listeners
-        verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture())
-
-        captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) }
-
-        verify(quickQsBrightnessController).refreshVisibility(anyBoolean())
-    }
-
-    @Test
     fun testMediaExpansionUpdatedWhenConfigurationChanged() {
         `when`(mediaFlags.useMediaSessionLayout()).thenReturn(true)
 
@@ -171,11 +158,10 @@
         metricsLogger: MetricsLogger,
         uiEventLogger: UiEventLoggerFake,
         qsLogger: QSLogger,
-        dumpManager: DumpManager,
-        quickQSBrightnessController: QuickQSBrightnessController
+        dumpManager: DumpManager
     ) : QuickQSPanelController(view, qsTileHost, qsCustomizerController, usingMediaPlayer,
         mediaHost, usingCollapsedLandscapeMedia, mediaFlags, metricsLogger, uiEventLogger, qsLogger,
-        dumpManager, quickQSBrightnessController) {
+        dumpManager) {
 
         private var rotation = RotationUtils.ROTATION_NONE
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index c20e887..ed35dcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -29,6 +29,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.wifitrackerlib.WifiEntry;
@@ -68,6 +69,8 @@
     private InternetAdapter mInternetAdapter;
     @Mock
     private InternetDialogController mInternetDialogController;
+    @Mock
+    private KeyguardStateController mKeyguard;
 
     private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
     private InternetDialog mInternetDialog;
@@ -100,7 +103,7 @@
 
         mInternetDialog = new InternetDialog(mContext, mock(InternetDialogFactory.class),
                 mInternetDialogController, true, true, true, mock(UiEventLogger.class), mHandler,
-                mBgExecutor);
+                mBgExecutor, mKeyguard);
         mInternetDialog.mAdapter = mInternetAdapter;
         mInternetDialog.mConnectedWifiEntry = mInternetWifiEntry;
         mInternetDialog.mWifiEntriesCount = mWifiEntries.size();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index d280f54..5d16036 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -36,14 +36,14 @@
         setFractionToShade(0f)
         setOnLockscreen(true)
 
-        shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10)
-        assertTrue(shelfState.actualWidth == 10)
+        shelf.updateActualWidth(/* fractionToShade */ 0f, /* shortestWidth */ 10f);
+        assertTrue(shelf.actualWidth == 10)
 
-        shelf.updateStateWidth(shelfState, /* fraction */ 0.5f, /* shortestWidth */ 10)
-        assertTrue(shelfState.actualWidth == 20)
+        shelf.updateActualWidth(/* fractionToShade */ 0.5f, /* shortestWidth */ 10f)
+        assertTrue(shelf.actualWidth == 20)
 
-        shelf.updateStateWidth(shelfState, /* fraction */ 1f, /* shortestWidth */ 10)
-        assertTrue(shelfState.actualWidth == 30)
+        shelf.updateActualWidth(/* fractionToShade */ 1f, /* shortestWidth */ 10f)
+        assertTrue(shelf.actualWidth == 30)
     }
 
     @Test
@@ -51,8 +51,8 @@
         setFractionToShade(0f)
         setOnLockscreen(false)
 
-        shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10)
-        assertTrue(shelfState.actualWidth == 30)
+        shelf.updateActualWidth(/* fraction */ 0f, /* shortestWidth */ 10f)
+        assertTrue(shelf.actualWidth == 30)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index c344aea..07e0279 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.policy
 
 import android.app.IActivityManager
-import android.app.IActivityTaskManager
 import android.app.admin.DevicePolicyManager
 import android.content.Context
 import android.content.DialogInterface
@@ -49,6 +48,7 @@
 import com.android.systemui.qs.user.UserSwitchDialogController
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView
+import com.android.systemui.statusbar.phone.ShadeController
 import com.android.systemui.telephony.TelephonyListenerManager
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.settings.SecureSettings
@@ -85,7 +85,6 @@
     @Mock private lateinit var userManager: UserManager
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
-    @Mock private lateinit var activityTaskManager: IActivityTaskManager
     @Mock private lateinit var telephonyListenerManager: TelephonyListenerManager
     @Mock private lateinit var secureSettings: SecureSettings
     @Mock private lateinit var falsingManager: FalsingManager
@@ -96,8 +95,10 @@
     @Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
     @Mock private lateinit var threadedRenderer: ThreadedRenderer
     @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+    @Mock private lateinit var shadeController: ShadeController
     private lateinit var testableLooper: TestableLooper
-    private lateinit var uiBgExecutor: FakeExecutor
+    private lateinit var bgExecutor: FakeExecutor
+    private lateinit var uiExecutor: FakeExecutor
     private lateinit var uiEventLogger: UiEventLoggerFake
     private lateinit var userSwitcherController: UserSwitcherController
     private lateinit var picture: Bitmap
@@ -116,10 +117,11 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         testableLooper = TestableLooper.get(this)
-        uiBgExecutor = FakeExecutor(FakeSystemClock())
+        bgExecutor = FakeExecutor(FakeSystemClock())
+        uiExecutor = FakeExecutor(FakeSystemClock())
         uiEventLogger = UiEventLoggerFake()
 
-        context.orCreateTestableResources.addOverride(
+        mContext.orCreateTestableResources.addOverride(
                 com.android.internal.R.bool.config_guestUserAutoCreated, false)
 
         mContext.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java))
@@ -141,12 +143,16 @@
 
         picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
 
+        // Create defaults for the current user
+        `when`(userTracker.userId).thenReturn(ownerId)
+        `when`(userTracker.userInfo).thenReturn(ownerInfo)
+
         setupController()
     }
 
     private fun setupController() {
         userSwitcherController = UserSwitcherController(
-                context,
+                mContext,
                 activityManager,
                 userManager,
                 userTracker,
@@ -159,14 +165,14 @@
                 uiEventLogger,
                 falsingManager,
                 telephonyListenerManager,
-                activityTaskManager,
                 secureSettings,
-                uiBgExecutor,
+                bgExecutor,
+                uiExecutor,
                 interactionJankMonitor,
                 latencyTracker,
                 dumpManager,
+                { shadeController },
                 dialogLaunchAnimator)
-        userSwitcherController.mPauseRefreshUsers = true
         userSwitcherController.init(notificationShadeWindowView)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
index dff77f3..5118637 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
@@ -195,6 +195,21 @@
     }
 
     @Test
+    public void addCallback_withMultipleInstancesOfTheSameCallback_registerOnlyOne() {
+        final Monitor monitor = new Monitor(mExecutor, new HashSet<>(), null /*callbacks*/);
+        final Monitor.Callback callback = mock(Monitor.Callback.class);
+
+        // Adds the same instance multiple times.
+        monitor.addCallback(callback);
+        monitor.addCallback(callback);
+        monitor.addCallback(callback);
+        mExecutor.runAllReady();
+
+        // Callback should only be triggered once.
+        verify(callback, times(1)).onConditionsChanged(true);
+    }
+
+    @Test
     public void removeCallback_shouldNoLongerReceiveUpdate() {
         final Condition condition = mock(Condition.class);
         final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)),
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
index dafe7a4..daead0a 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
@@ -58,11 +58,14 @@
 
     private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
 
+    private final Context mContext;
+
     public CloudSearchManagerService(Context context) {
         super(context, new FrameworkResourcesServiceNameResolver(context,
                         R.string.config_defaultCloudSearchService), null,
                 PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
         mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+        mContext = context;
     }
 
     @Override
@@ -106,6 +109,8 @@
         @Override
         public void search(@NonNull SearchRequest searchRequest,
                 @NonNull ICloudSearchManagerCallback callBack) {
+            searchRequest.setSource(
+                    mContext.getPackageManager().getNameForUid(Binder.getCallingUid()));
             runForUserLocked("search", searchRequest.getRequestId(), (service) ->
                     service.onSearchLocked(searchRequest, callBack));
         }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 2f6a46a..387d911 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -606,6 +606,10 @@
         }
     }
 
+    boolean isDisplayOwnedByVirtualDevice(int displayId) {
+        return mVirtualDisplayIds.contains(displayId);
+    }
+
     interface OnDeviceCloseListener {
         void onClose(int associationId);
     }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 7842697..c7d8daa 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -348,6 +348,19 @@
             }
             return false;
         }
+
+        @Override
+        public boolean isDisplayOwnedByAnyVirtualDevice(int displayId) {
+            synchronized (mVirtualDeviceManagerLock) {
+                int size = mVirtualDevices.size();
+                for (int i = 0; i < size; i++) {
+                    if (mVirtualDevices.valueAt(i).isDisplayOwnedByVirtualDevice(displayId)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
     }
 
     private static final class PendingTrampolineMap {
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index a8eeaf8..e335a16 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -49,6 +49,7 @@
 import com.android.server.pm.pkg.AndroidPackageApi;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
 import com.android.server.pm.pkg.component.ParsedMainComponent;
 import com.android.server.pm.pkg.mutate.PackageStateMutator;
 
@@ -1262,6 +1263,21 @@
             boolean migrateAppsData);
 
     /**
+     * Returns an array of PackageStateInternal that are all part of a shared user setting which is
+     * denoted by the app ID. Returns an empty set if the shared user setting doesn't exist or does
+     * not contain any package.
+     */
+    @NonNull
+    public abstract ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId);
+
+    /**
+     * Returns the SharedUserApi denoted by the app ID of the shared user setting. Returns null if
+     * the corresponding shared user setting doesn't exist.
+     */
+    @Nullable
+    public abstract SharedUserApi getSharedUserApi(int sharedUserAppId);
+
+    /**
      * Initiates a package state mutation request, returning the current state as known by
      * PackageManager. This allows the later commit request to compare the initial values and
      * determine if any state was changed or any packages were updated since the whole request
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 454028d..c194527 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -134,6 +134,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
@@ -190,6 +191,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -471,6 +473,9 @@
     @GuardedBy("mLock")
     private String mMoveTargetUuid;
 
+    @GuardedBy("mCloudMediaProviders")
+    private final SparseArray<String> mCloudMediaProviders = new SparseArray<>();
+
     private volatile int mMediaStoreAuthorityAppId = -1;
 
     private volatile int mDownloadsAuthorityAppId = -1;
@@ -752,6 +757,7 @@
     private static final int H_BOOT_COMPLETED = 13;
     private static final int H_COMPLETE_UNLOCK_USER = 14;
     private static final int H_VOLUME_STATE_CHANGED = 15;
+    private static final int H_CLOUD_MEDIA_PROVIDER_CHANGED = 16;
 
     class StorageManagerServiceHandler extends Handler {
         public StorageManagerServiceHandler(Looper looper) {
@@ -879,6 +885,17 @@
                     final SomeArgs args = (SomeArgs) msg.obj;
                     onVolumeStateChangedAsync((VolumeInfo) args.arg1, args.argi1, args.argi2);
                     args.recycle();
+                    break;
+                }
+                case H_CLOUD_MEDIA_PROVIDER_CHANGED: {
+                    final Object listener = msg.obj;
+                    if (listener instanceof StorageManagerInternal.CloudProviderChangeListener) {
+                        notifyCloudMediaProviderChangedAsync(
+                                (StorageManagerInternal.CloudProviderChangeListener) listener);
+                    } else {
+                        onCloudMediaProviderChangedAsync(msg.arg1);
+                    }
+                    break;
                 }
             }
         }
@@ -1697,7 +1714,6 @@
         return true;
     }
 
-
     private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
         if (vol.type == VolumeInfo.TYPE_EMULATED) {
             if (newState != VolumeInfo.STATE_MOUNTED) {
@@ -1836,6 +1852,27 @@
         }
     }
 
+    private void notifyCloudMediaProviderChangedAsync(
+            @NonNull StorageManagerInternal.CloudProviderChangeListener listener) {
+        synchronized (mCloudMediaProviders) {
+            for (int i = mCloudMediaProviders.size() - 1; i >= 0; --i) {
+                listener.onCloudProviderChanged(
+                        mCloudMediaProviders.keyAt(i), mCloudMediaProviders.valueAt(i));
+            }
+        }
+    }
+
+    private void onCloudMediaProviderChangedAsync(int userId) {
+        final String authority;
+        synchronized (mCloudMediaProviders) {
+            authority = mCloudMediaProviders.get(userId);
+        }
+        for (StorageManagerInternal.CloudProviderChangeListener listener :
+                mStorageManagerInternal.mCloudProviderChangeListeners) {
+            listener.onCloudProviderChanged(userId, authority);
+        }
+    }
+
     private void maybeLogMediaMount(VolumeInfo vol, int newState) {
         if (!SecurityLog.isLoggingEnabled()) {
             return;
@@ -3773,7 +3810,7 @@
             Binder.restoreCallingIdentity(token);
         }
     }
-    
+
     @Override
     public void notifyAppIoBlocked(String volumeUuid, int uid, int tid, int reason) {
         enforceExternalStorageService();
@@ -3794,11 +3831,46 @@
         return isAppIoBlocked(uid);
     }
 
-
     private boolean isAppIoBlocked(int uid) {
         return mStorageSessionController.isAppIoBlocked(uid);
     }
 
+    @Override
+    public void setCloudMediaProvider(@Nullable String authority) {
+        enforceExternalStorageService();
+
+        final int userId = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized (mCloudMediaProviders) {
+            final String oldAuthority = mCloudMediaProviders.get(userId);
+            if (!Objects.equals(authority, oldAuthority)) {
+                mCloudMediaProviders.put(userId, authority);
+                mHandler.obtainMessage(H_CLOUD_MEDIA_PROVIDER_CHANGED, userId, 0, authority)
+                        .sendToTarget();
+            }
+        }
+    }
+
+    @Override
+    @Nullable
+    public String getCloudMediaProvider() {
+        final int callingUid = Binder.getCallingUid();
+        final int userId = UserHandle.getUserId(callingUid);
+        final String authority;
+        synchronized (mCloudMediaProviders) {
+            authority = mCloudMediaProviders.get(userId);
+        }
+        if (authority == null) {
+            return null;
+        }
+        final ProviderInfo pi = mPmInternal.resolveContentProvider(
+                authority, 0, userId, callingUid);
+        if (pi == null
+                || mPmInternal.filterAppAccess(pi.packageName, callingUid, userId)) {
+            return null;
+        }
+        return authority;
+    }
+
     /**
      * Enforces that the caller is the {@link ExternalStorageService}
      *
@@ -4951,6 +5023,12 @@
             pw.decreaseIndent();
         }
 
+        synchronized (mCloudMediaProviders) {
+            pw.println();
+            pw.print("Media cloud providers: ");
+            pw.println(mCloudMediaProviders);
+        }
+
         pw.println();
         pw.print("Last maintenance: ");
         pw.println(TimeUtils.formatForLogging(mLastMaintenance));
@@ -4971,6 +5049,9 @@
         private final List<StorageManagerInternal.ResetListener> mResetListeners =
                 new ArrayList<>();
 
+        private final CopyOnWriteArraySet<StorageManagerInternal.CloudProviderChangeListener>
+                mCloudProviderChangeListeners = new CopyOnWriteArraySet<>();
+
         @Override
         public boolean isFuseMounted(int userId) {
             synchronized (mLock) {
@@ -5193,5 +5274,12 @@
                 return mCeStoragePreparedUsers.contains(userId);
             }
         }
+
+        @Override
+        public void registerCloudProviderChangeListener(
+                @NonNull StorageManagerInternal.CloudProviderChangeListener listener) {
+            mCloudProviderChangeListeners.add(listener);
+            mHandler.obtainMessage(H_CLOUD_MEDIA_PROVIDER_CHANGED, listener);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 940ad73..e2e53cb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -20,6 +20,8 @@
 import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY;
 
 import android.annotation.NonNull;
 import android.app.ActivityThread;
@@ -203,6 +205,10 @@
     private static final boolean DEFAULT_ENABLE_COMPONENT_ALIAS = false;
     private static final String DEFAULT_COMPONENT_ALIAS_OVERRIDES = "";
 
+    private static final int DEFAULT_DEFER_BOOT_COMPLETED_BROADCAST =
+            DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY
+            | DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY;
+
     // Flag stored in the DeviceConfig API.
     /**
      * Maximum number of cached processes.
@@ -292,6 +298,9 @@
      */
     private static final String KEY_PROCESS_KILL_TIMEOUT = "process_kill_timeout";
 
+    private static final String KEY_DEFER_BOOT_COMPLETED_BROADCAST =
+            "defer_boot_completed_broadcast";
+
     // Maximum number of cached processes we will allow.
     public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
 
@@ -603,6 +612,15 @@
     volatile boolean mEnableComponentAlias = DEFAULT_ENABLE_COMPONENT_ALIAS;
 
     /**
+     * Where or not to defer LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts until the first
+     * time the process of the UID is started.
+     * Defined in {@link BroadcastConstants#DeferBootCompletedBroadcastType}
+     */
+    @GuardedBy("mService")
+    volatile @BroadcastConstants.DeferBootCompletedBroadcastType int mDeferBootCompletedBroadcast =
+            DEFAULT_DEFER_BOOT_COMPLETED_BROADCAST;
+
+    /**
      * Defines component aliases. Format
      * ComponentName ":" ComponentName ( "," ComponentName ":" ComponentName )*
      */
@@ -846,6 +864,9 @@
                             case KEY_PROCESS_KILL_TIMEOUT:
                                 updateProcessKillTimeout();
                                 break;
+                            case KEY_DEFER_BOOT_COMPLETED_BROADCAST:
+                                updateDeferBootCompletedBroadcast();
+                                break;
                             default:
                                 break;
                         }
@@ -1258,6 +1279,13 @@
         }
     }
 
+    private void updateDeferBootCompletedBroadcast() {
+        mDeferBootCompletedBroadcast = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_DEFER_BOOT_COMPLETED_BROADCAST,
+                DEFAULT_DEFER_BOOT_COMPLETED_BROADCAST);
+    }
+
     private long[] parseLongArray(@NonNull String key, @NonNull long[] def) {
         final String val = DeviceConfig.getString(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 key, null);
@@ -1529,6 +1557,8 @@
         pw.print("="); pw.println(mEnableComponentAlias);
         pw.print("  "); pw.print(KEY_COMPONENT_ALIAS_OVERRIDES);
         pw.print("="); pw.println(mComponentAliasOverrides);
+        pw.print("  "); pw.print(KEY_DEFER_BOOT_COMPLETED_BROADCAST);
+        pw.print("="); pw.println(mDeferBootCompletedBroadcast);
 
         pw.println();
         if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a478c31..2371101 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4856,6 +4856,10 @@
             }
         }
 
+        if (!badApp) {
+            updateUidReadyForBootCompletedBroadcastLocked(app.uid);
+        }
+
         // Check if a next-broadcast receiver is in this process...
         if (!badApp && isPendingBroadcastProcessLocked(pid)) {
             try {
@@ -6482,7 +6486,8 @@
         return isBackgroundRestrictedNoCheck(callingUid, packageName);
     }
 
-    boolean isBackgroundRestrictedNoCheck(final int uid, final String packageName) {
+    @VisibleForTesting
+    public boolean isBackgroundRestrictedNoCheck(final int uid, final String packageName) {
         final int mode = getAppOpsManager().checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
                 uid, packageName);
         return mode != AppOpsManager.MODE_ALLOWED;
@@ -12764,6 +12769,13 @@
         return didSomething;
     }
 
+    void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
+        for (BroadcastQueue queue : mBroadcastQueues) {
+            queue.updateUidReadyForBootCompletedBroadcastLocked(uid);
+            queue.scheduleBroadcastsLocked();
+        }
+    }
+
     /**
      * @deprecated Use {@link #registerReceiverWithFeature}
      */
@@ -13389,10 +13401,8 @@
                 }
             }
 
-            // TODO (206518114): We need to use the "real" package name which sent the broadcast,
-            // in case the broadcast is sent via PendingIntent.
             if (brOptions.getIdForResponseEvent() > 0) {
-                enforceUsageStatsPermission(callerPackage, realCallingUid, realCallingPid,
+                enforceUsageStatsPermission(callerPackage, callingUid, callingPid,
                         "recordResponseEventWhileInBackground()");
             }
         }
diff --git a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
index 3c780aa..d1cf004 100644
--- a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
@@ -20,7 +20,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.AppBatteryTracker.BATTERY_USAGE_NONE;
 import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
-import static com.android.server.am.BaseAppStateDurationsTracker.EVENT_NUM;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_NUM;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -36,9 +36,9 @@
 import com.android.server.am.AppBatteryTracker.AppBatteryPolicy;
 import com.android.server.am.AppBatteryTracker.BatteryUsage;
 import com.android.server.am.AppBatteryTracker.ImmutableBatteryUsage;
-import com.android.server.am.BaseAppStateDurationsTracker.EventListener;
 import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
 import com.android.server.am.BaseAppStateTracker.Injector;
+import com.android.server.am.BaseAppStateTracker.StateListener;
 
 import java.io.PrintWriter;
 import java.lang.reflect.Constructor;
@@ -57,13 +57,13 @@
  */
 final class AppBatteryExemptionTracker
         extends BaseAppStateDurationsTracker<AppBatteryExemptionPolicy, UidBatteryStates>
-        implements BaseAppStateEvents.Factory<UidBatteryStates>, EventListener {
+        implements BaseAppStateEvents.Factory<UidBatteryStates>, StateListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "AppBatteryExemptionTracker" : TAG_AM;
 
     private static final boolean DEBUG_BACKGROUND_BATTERY_EXEMPTION_TRACKER = false;
 
     // As it's a UID-based tracker, anywhere which requires a package name, use this default name.
-    private static final String DEFAULT_NAME = "";
+    static final String DEFAULT_NAME = "";
 
     AppBatteryExemptionTracker(Context context, AppRestrictionController controller) {
         this(context, controller, null, null);
@@ -80,9 +80,7 @@
     void onSystemReady() {
         super.onSystemReady();
         mAppRestrictionController.forEachTracker(tracker -> {
-            if (tracker instanceof BaseAppStateDurationsTracker) {
-                ((BaseAppStateDurationsTracker) tracker).registerEventListener(this);
-            }
+            tracker.registerStateListener(this);
         });
     }
 
@@ -97,19 +95,20 @@
     }
 
     @Override
-    public void onNewEvent(int uid, String packageName, boolean start, long now, int eventType) {
+    public void onStateChange(int uid, String packageName, boolean start, long now, int stateType) {
         if (!mInjector.getPolicy().isEnabled()) {
             return;
         }
         final ImmutableBatteryUsage batteryUsage = mAppRestrictionController
                 .getUidBatteryUsage(uid);
+        final int stateTypeIndex = stateTypeToIndex(stateType);
         synchronized (mLock) {
             UidBatteryStates pkg = mPkgEvents.get(uid, DEFAULT_NAME);
             if (pkg == null) {
                 pkg = createAppStateEvents(uid, DEFAULT_NAME);
                 mPkgEvents.put(uid, DEFAULT_NAME, pkg);
             }
-            pkg.addEvent(start, now, batteryUsage, eventType);
+            pkg.addEvent(start, now, batteryUsage, stateTypeIndex);
         }
     }
 
@@ -125,7 +124,8 @@
      * @return The to-be-exempted battery usage of the given UID in the given duration; it could
      *         be considered as "exempted" due to various use cases, i.e. media playback.
      */
-    ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now) {
+    ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now,
+            int types) {
         if (!mInjector.getPolicy().isEnabled()) {
             return BATTERY_USAGE_NONE;
         }
@@ -135,7 +135,7 @@
             if (pkg == null) {
                 return BATTERY_USAGE_NONE;
             }
-            result = pkg.getBatteryUsageSince(since, now);
+            result = pkg.getBatteryUsageSince(since, now, types);
         }
         if (!result.second.isEmpty()) {
             // We have an open event (just start, no stop), get the battery usage till now.
@@ -149,7 +149,7 @@
     static final class UidBatteryStates extends BaseAppStateDurations<UidStateEventWithBattery> {
         UidBatteryStates(int uid, @NonNull String tag,
                 @NonNull MaxTrackingDurationConfig maxTrackingDurationConfig) {
-            super(uid, DEFAULT_NAME, EVENT_NUM, tag, maxTrackingDurationConfig);
+            super(uid, DEFAULT_NAME, STATE_TYPE_NUM, tag, maxTrackingDurationConfig);
         }
 
         UidBatteryStates(@NonNull UidBatteryStates other) {
@@ -160,7 +160,7 @@
          * @param start {@code true} if it's a start event.
          * @param now   The timestamp when this event occurred.
          * @param batteryUsage The background current drain since the system boots.
-         * @param eventType One of EVENT_TYPE_* defined in the class BaseAppStateDurationsTracker.
+         * @param eventType One of STATE_TYPE_INDEX_* defined in the class BaseAppStateTracker.
          */
         void addEvent(boolean start, long now, ImmutableBatteryUsage batteryUsage, int eventType) {
             if (start) {
@@ -184,17 +184,6 @@
             return mEvents[eventType] != null ? mEvents[eventType].peekLast() : null;
         }
 
-        /**
-         * @return The pair of bg battery usage of given duration; the first value in the pair
-         *         is the aggregated battery usage of all event pairs in this duration; while
-         *         the second value is the battery usage since the system boots, if there is
-         *         an open event(just start, no stop) at the end of the duration.
-         */
-        Pair<ImmutableBatteryUsage, ImmutableBatteryUsage> getBatteryUsageSince(long since,
-                long now, int eventType) {
-            return getBatteryUsageSince(since, now, mEvents[eventType]);
-        }
-
         private Pair<ImmutableBatteryUsage, ImmutableBatteryUsage> getBatteryUsageSince(long since,
                 long now, LinkedList<UidStateEventWithBattery> events) {
             if (events == null || events.size() == 0) {
@@ -217,13 +206,18 @@
         }
 
         /**
-         * @return The aggregated battery usage amongst all the event types we're tracking.
+         * @return The pair of bg battery usage of given duration; the first value in the pair
+         *         is the aggregated battery usage of selected events in this duration; while
+         *         the second value is the battery usage since the system boots, if there is
+         *         an open event(just start, no stop) at the end of the duration.
          */
         Pair<ImmutableBatteryUsage, ImmutableBatteryUsage> getBatteryUsageSince(long since,
-                long now) {
+                long now, int types) {
             LinkedList<UidStateEventWithBattery> result = new LinkedList<>();
             for (int i = 0; i < mEvents.length; i++) {
-                result = add(result, mEvents[i]);
+                if ((types & stateIndexToType(i)) != 0) {
+                    result = add(result, mEvents[i]);
+                }
             }
             return getBatteryUsageSince(since, now, result);
         }
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 6492662..655e309 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -325,7 +325,8 @@
                 final int uid = uidConsumers.keyAt(i);
                 final ImmutableBatteryUsage actualUsage = uidConsumers.valueAt(i);
                 final ImmutableBatteryUsage exemptedUsage = mAppRestrictionController
-                        .getUidBatteryExemptedUsageSince(uid, since, now);
+                        .getUidBatteryExemptedUsageSince(uid, since, now,
+                                bgPolicy.mBgCurrentDrainExemptedTypes);
                 // It's possible the exemptedUsage could be larger than actualUsage,
                 // as the former one is an approximate value.
                 final BatteryUsage bgUsage = actualUsage.mutate()
@@ -656,7 +657,8 @@
                     final BatteryUsage bgUsage = uidConsumers.valueAt(i)
                             .calcPercentage(uid, bgPolicy);
                     final BatteryUsage exemptedUsage = mAppRestrictionController
-                            .getUidBatteryExemptedUsageSince(uid, since, now)
+                            .getUidBatteryExemptedUsageSince(uid, since, now,
+                                    bgPolicy.mBgCurrentDrainExemptedTypes)
                             .calcPercentage(uid, bgPolicy);
                     final BatteryUsage reportedUsage = new BatteryUsage(bgUsage)
                             .subtract(exemptedUsage)
@@ -1027,6 +1029,21 @@
                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_power_components";
 
         /**
+         * The types of state where we'll exempt its battery usage when it's in that state.
+         * The state here must be one or a combination of STATE_TYPE_* in BaseAppStateTracker.
+         */
+        static final String KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES =
+                DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_exempted_types";
+
+        /**
+         * The behavior when an app has the permission ACCESS_BACKGROUND_LOCATION granted,
+         * whether or not the system will use a higher threshold towards its background battery
+         * usage because of it.
+         */
+        static final String KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION =
+                DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_high_threshold_by_bg_location";
+
+        /**
          * Default value to the {@link #INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD} of
          * the {@link #mBgCurrentDrainRestrictedBucketThreshold}.
          */
@@ -1089,6 +1106,16 @@
         final int mDefaultBgCurrentDrainPowerComponent;
 
         /**
+         * Default value to {@link #mBgCurrentDrainExmptedTypes}.
+         **/
+        final int mDefaultBgCurrentDrainExemptedTypes;
+
+        /**
+         * Default value to {@link #mBgCurrentDrainHighThresholdByBgLocation}.
+         */
+        final boolean mDefaultBgCurrentDrainHighThresholdByBgLocation;
+
+        /**
          * The index to {@link #mBgCurrentDrainRestrictedBucketThreshold}
          * and {@link #mBgCurrentDrainBgRestrictedThreshold}.
          */
@@ -1146,6 +1173,16 @@
         volatile Dimensions[] mBatteryDimensions;
 
         /**
+         * @see #KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES.
+         */
+        volatile int mBgCurrentDrainExemptedTypes;
+
+        /**
+         * @see #KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION.
+         */
+        volatile boolean mBgCurrentDrainHighThresholdByBgLocation;
+
+        /**
          * The capacity of the battery when fully charged in mAh.
          */
         private int mBatteryFullChargeMah;
@@ -1201,6 +1238,10 @@
                     R.integer.config_bg_current_drain_types_to_bg_restricted);
             mDefaultBgCurrentDrainPowerComponent = resources.getInteger(
                     R.integer.config_bg_current_drain_power_components);
+            mDefaultBgCurrentDrainExemptedTypes = resources.getInteger(
+                    R.integer.config_bg_current_drain_exempted_types);
+            mDefaultBgCurrentDrainHighThresholdByBgLocation = resources.getBoolean(
+                    R.bool.config_bg_current_drain_high_threshold_by_bg_location);
             mBgCurrentDrainRestrictedBucketThreshold[0] =
                     mDefaultBgCurrentDrainRestrictedBucket;
             mBgCurrentDrainRestrictedBucketThreshold[1] =
@@ -1230,6 +1271,7 @@
             switch (name) {
                 case KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET:
                 case KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED:
+                case KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION:
                 case KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_RESTRICTED_BUCKET:
                 case KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_BG_RESTRICTED:
                 case KEY_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET:
@@ -1249,6 +1291,9 @@
                 case KEY_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED:
                     updateCurrentDrainEventDurationBasedThresholdEnabled();
                     break;
+                case KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES:
+                    updateCurrentDrainExemptedTypes();
+                    break;
                 default:
                     super.onPropertiesChanged(name);
                     break;
@@ -1305,6 +1350,10 @@
                     mBatteryDimensions[i] = new Dimensions(mBgCurrentDrainPowerComponents, i);
                 }
             }
+            mBgCurrentDrainHighThresholdByBgLocation =
+                    DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION,
+                    mDefaultBgCurrentDrainHighThresholdByBgLocation);
         }
 
         private void updateCurrentDrainWindow() {
@@ -1335,6 +1384,13 @@
                     mDefaultBgCurrentDrainEventDurationBasedThresholdEnabled);
         }
 
+        private void updateCurrentDrainExemptedTypes() {
+            mBgCurrentDrainExemptedTypes = DeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES,
+                    mDefaultBgCurrentDrainExemptedTypes);
+        }
+
         @Override
         public void onSystemReady() {
             mBatteryFullChargeMah =
@@ -1345,6 +1401,7 @@
             updateCurrentDrainMediaPlaybackMinDuration();
             updateCurrentDrainLocationMinDuration();
             updateCurrentDrainEventDurationBasedThresholdEnabled();
+            updateCurrentDrainExemptedTypes();
         }
 
         @Override
@@ -1454,16 +1511,18 @@
                         notifyController = true;
                     } else {
                         excessive = true;
-                        if (brPercentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex]) {
+                        if (brPercentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex]
+                                && curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
                             // If we're in the restricted standby bucket but still seeing high
                             // current drains, tell the controller again.
-                            if (curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET
-                                    && ts[TIME_STAMP_INDEX_BG_RESTRICTED] == 0) {
-                                if (now > ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET]
-                                        + mBgCurrentDrainWindowMs) {
-                                    ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now;
-                                    notifyController = true;
-                                }
+                            final long lastResbucket = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET];
+                            final long lastBgRes = ts[TIME_STAMP_INDEX_BG_RESTRICTED];
+                            // If it has been a while since restricting the app and since the last
+                            // time we notify the controller, notify it again.
+                            if ((now >= lastResbucket + mBgCurrentDrainWindowMs) && (lastBgRes == 0
+                                    || (now >= lastBgRes + mBgCurrentDrainWindowMs))) {
+                                ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now;
+                                notifyController = true;
                             }
                         }
                     }
@@ -1505,6 +1564,9 @@
         }
 
         private boolean hasLocation(int uid, long now, long window) {
+            if (!mBgCurrentDrainHighThresholdByBgLocation) {
+                return false;
+            }
             final AppRestrictionController controller = mTracker.mAppRestrictionController;
             if (mInjector.getPermissionManagerServiceInternal().checkUidPermission(
                     uid, ACCESS_BACKGROUND_LOCATION) == PERMISSION_GRANTED) {
@@ -1618,6 +1680,14 @@
                 pw.print(KEY_BG_CURRENT_DRAIN_POWER_COMPONENTS);
                 pw.print('=');
                 pw.println(mBgCurrentDrainPowerComponents);
+                pw.print(prefix);
+                pw.print(KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES);
+                pw.print('=');
+                pw.println(BaseAppStateTracker.stateTypesToString(mBgCurrentDrainExemptedTypes));
+                pw.print(prefix);
+                pw.print(KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION);
+                pw.print('=');
+                pw.println(mBgCurrentDrainHighThresholdByBgLocation);
 
                 pw.print(prefix);
                 pw.println("Excessive current drain detected:");
diff --git a/services/core/java/com/android/server/am/AppFGSTracker.java b/services/core/java/com/android/server/am/AppFGSTracker.java
index 9c775b3..5ac5a70 100644
--- a/services/core/java/com/android/server/am/AppFGSTracker.java
+++ b/services/core/java/com/android/server/am/AppFGSTracker.java
@@ -16,8 +16,6 @@
 
 package com.android.server.am;
 
-import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE;
@@ -43,6 +41,7 @@
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -54,6 +53,7 @@
 import com.android.server.am.BaseAppStateEventsTracker.BaseAppStateEventsPolicy;
 import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
 import com.android.server.am.BaseAppStateTracker.Injector;
+import com.android.server.notification.NotificationManagerInternal;
 
 import java.io.PrintWriter;
 import java.lang.reflect.Constructor;
@@ -71,6 +71,9 @@
 
     private final MyHandler mHandler;
 
+    @GuardedBy("mLock")
+    private final UidProcessMap<ArraySet<Integer>> mFGSNotificationIDs = new UidProcessMap<>();
+
     // Unlocked since it's only accessed in single thread.
     private final ArrayMap<PackageDurations, Long> mTmpPkgDurations = new ArrayMap<>();
 
@@ -100,11 +103,19 @@
                 : MyHandler.MSG_FOREGROUND_SERVICES_STOPPED, pid, uid, packageName).sendToTarget();
     }
 
+    @Override
+    public void onForegroundServiceNotificationUpdated(String packageName, int uid,
+            int foregroundId) {
+        mHandler.obtainMessage(MyHandler.MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED,
+                uid, foregroundId, packageName).sendToTarget();
+    }
+
     private static class MyHandler extends Handler {
         static final int MSG_FOREGROUND_SERVICES_STARTED = 0;
         static final int MSG_FOREGROUND_SERVICES_STOPPED = 1;
         static final int MSG_FOREGROUND_SERVICES_CHANGED = 2;
-        static final int MSG_CHECK_LONG_RUNNING_FGS = 3;
+        static final int MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED = 3;
+        static final int MSG_CHECK_LONG_RUNNING_FGS = 4;
 
         private final AppFGSTracker mTracker;
 
@@ -128,6 +139,10 @@
                     mTracker.handleForegroundServicesChanged(
                             (String) msg.obj, msg.arg1, msg.arg2);
                     break;
+                case MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED:
+                    mTracker.handleForegroundServiceNotificationUpdated(
+                            (String) msg.obj, msg.arg1, msg.arg2);
+                    break;
                 case MSG_CHECK_LONG_RUNNING_FGS:
                     mTracker.checkLongRunningFgs();
                     break;
@@ -205,6 +220,44 @@
         }
     }
 
+    private void handleForegroundServiceNotificationUpdated(String packageName, int uid,
+            int notificationId) {
+        synchronized (mLock) {
+            if (notificationId > 0) {
+                ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName);
+                if (notificationIDs == null) {
+                    notificationIDs = new ArraySet<>();
+                    mFGSNotificationIDs.put(uid, packageName, notificationIDs);
+                }
+                notificationIDs.add(notificationId);
+            } else if (notificationId < 0) {
+                final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName);
+                if (notificationIDs != null) {
+                    notificationIDs.remove(-notificationId);
+                    if (notificationIDs.isEmpty()) {
+                        mFGSNotificationIDs.remove(uid, packageName);
+                    }
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean hasForegroundServiceNotificationsLocked(String packageName, int uid) {
+        final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName);
+        if (notificationIDs == null || notificationIDs.isEmpty()) {
+            return false;
+        }
+        final NotificationManagerInternal nm = mInjector.getNotificationManagerInternal();
+        final int userId = UserHandle.getUserId(uid);
+        for (int i = notificationIDs.size() - 1; i >= 0; i--) {
+            if (nm.isNotificationShown(packageName, null, notificationIDs.valueAt(i), userId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @GuardedBy("mLock")
     private void scheduleDurationCheckLocked(long now) {
         // Look for the active FGS with longest running time till now.
@@ -375,6 +428,28 @@
         }
     }
 
+    boolean hasForegroundServiceNotifications(String packageName, int uid) {
+        synchronized (mLock) {
+            return hasForegroundServiceNotificationsLocked(packageName, uid);
+        }
+    }
+
+    boolean hasForegroundServiceNotifications(int uid) {
+        synchronized (mLock) {
+            final SparseArray<ArrayMap<String, ArraySet<Integer>>> map =
+                    mFGSNotificationIDs.getMap();
+            final ArrayMap<String, ArraySet<Integer>> pkgs = map.get(uid);
+            if (pkgs != null) {
+                for (int i = pkgs.size() - 1; i >= 0; i--) {
+                    if (hasForegroundServiceNotificationsLocked(pkgs.keyAt(i), uid)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
     @Override
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix);
@@ -382,6 +457,35 @@
         super.dump(pw, "  " + prefix);
     }
 
+    @Override
+    void dumpOthers(PrintWriter pw, String prefix) {
+        pw.print(prefix);
+        pw.println("APPS WITH ACTIVE FOREGROUND SERVICES:");
+        prefix = "  " + prefix;
+        synchronized (mLock) {
+            final SparseArray<ArrayMap<String, ArraySet<Integer>>> map =
+                    mFGSNotificationIDs.getMap();
+            if (map.size() == 0) {
+                pw.print(prefix);
+                pw.println("(none)");
+            }
+            for (int i = 0, size = map.size(); i < size; i++) {
+                final int uid = map.keyAt(i);
+                final String uidString = UserHandle.formatUid(uid);
+                final ArrayMap<String, ArraySet<Integer>> pkgs = map.valueAt(i);
+                for (int j = 0, numOfPkgs = pkgs.size(); j < numOfPkgs; j++) {
+                    final String pkgName = pkgs.keyAt(j);
+                    pw.print(prefix);
+                    pw.print(pkgName);
+                    pw.print('/');
+                    pw.print(uidString);
+                    pw.print(" notification=");
+                    pw.println(hasForegroundServiceNotificationsLocked(pkgName, uid));
+                }
+            }
+        }
+    }
+
     /**
      * Tracks the durations with active FGS for a given package.
      */
@@ -437,7 +541,7 @@
                     }
                     if (isActive(i)) {
                         mEvents[i].add(new BaseTimeEvent(now));
-                        notifyListenersOnEventIfNecessary(false, now,
+                        notifyListenersOnStateChangeIfNecessary(false, now,
                                 indexToForegroundServiceType(i));
                     }
                 }
@@ -463,13 +567,13 @@
                     }
                     if (!isActive(i)) {
                         mEvents[i].add(new BaseTimeEvent(now));
-                        notifyListenersOnEventIfNecessary(true, now, serviceType);
+                        notifyListenersOnStateChangeIfNecessary(true, now, serviceType);
                     }
                 } else {
                     // Stop this type.
                     if (mEvents[i] != null && isActive(i)) {
                         mEvents[i].add(new BaseTimeEvent(now));
-                        notifyListenersOnEventIfNecessary(false, now, serviceType);
+                        notifyListenersOnStateChangeIfNecessary(false, now, serviceType);
                     }
                 }
                 changes &= ~serviceType;
@@ -478,20 +582,20 @@
             mForegroundServiceTypes = serviceTypes;
         }
 
-        private void notifyListenersOnEventIfNecessary(boolean start, long now,
+        private void notifyListenersOnStateChangeIfNecessary(boolean start, long now,
                 @ForegroundServiceType int serviceType) {
-            int eventType;
+            int stateType;
             switch (serviceType) {
                 case FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK:
-                    eventType = BaseAppStateDurationsTracker.EVENT_TYPE_FGS_MEDIA_PLAYBACK;
+                    stateType = BaseAppStateDurationsTracker.STATE_TYPE_FGS_MEDIA_PLAYBACK;
                     break;
                 case FOREGROUND_SERVICE_TYPE_LOCATION:
-                    eventType = BaseAppStateDurationsTracker.EVENT_TYPE_FGS_LOCATION;
+                    stateType = BaseAppStateDurationsTracker.STATE_TYPE_FGS_LOCATION;
                     break;
                 default:
                     return;
             }
-            mTracker.notifyListenersOnEvent(mUid, mPackageName, start, now, eventType);
+            mTracker.notifyListenersOnStateChange(mUid, mPackageName, start, now, stateType);
         }
 
         void setIsLongRunning(boolean isLongRunning) {
@@ -687,10 +791,6 @@
             if (shouldExemptLocationFGS(packageName, uid, now, since)) {
                 return;
             }
-            if (hasBackgroundLocationPermission(packageName, uid)) {
-                // This package has background location permission, ignore it.
-                return;
-            }
             mTracker.mAppRestrictionController.postLongRunningFgsIfNecessary(packageName, uid);
         }
 
@@ -723,19 +823,6 @@
             return false;
         }
 
-        boolean hasBackgroundLocationPermission(String packageName, int uid) {
-            if (mInjector.getPermissionManagerServiceInternal().checkPermission(
-                    packageName, ACCESS_BACKGROUND_LOCATION, UserHandle.getUserId(uid))
-                    == PERMISSION_GRANTED) {
-                if (DEBUG_BACKGROUND_FGS_TRACKER) {
-                    Slog.i(TAG, "Ignoring bg-location FGS in " + packageName + "/"
-                            + UserHandle.formatUid(uid));
-                }
-                return true;
-            }
-            return false;
-        }
-
         @Override
         String getExemptionReasonString(String packageName, int uid, @ReasonCode int reason) {
             if (reason != REASON_DENIED) {
@@ -745,8 +832,7 @@
             final long window = getFgsLongRunningWindowSize();
             final long since = Math.max(0, now - getFgsLongRunningWindowSize());
             return "{mediaPlayback=" + shouldExemptMediaPlaybackFGS(packageName, uid, now, window)
-                    + ", location=" + shouldExemptLocationFGS(packageName, uid, now, since)
-                    + ", bgLocationPerm=" + hasBackgroundLocationPermission(packageName, uid) + "}";
+                    + ", location=" + shouldExemptLocationFGS(packageName, uid, now, since) + "}";
         }
 
         void onLongRunningFgsGone(String packageName, int uid) {
diff --git a/services/core/java/com/android/server/am/AppMediaSessionTracker.java b/services/core/java/com/android/server/am/AppMediaSessionTracker.java
index 3914f6f..4ce23f7 100644
--- a/services/core/java/com/android/server/am/AppMediaSessionTracker.java
+++ b/services/core/java/com/android/server/am/AppMediaSessionTracker.java
@@ -19,8 +19,8 @@
 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.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
-import static com.android.server.am.BaseAppStateDurationsTracker.EVENT_TYPE_MEDIA_SESSION;
 import static com.android.server.am.BaseAppStateTracker.ONE_DAY;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_MEDIA_SESSION;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -104,8 +104,8 @@
                     }
                     if (!pkg.isActive()) {
                         pkg.addEvent(true, now);
-                        notifyListenersOnEvent(pkg.mUid, pkg.mPackageName, true, now,
-                                EVENT_TYPE_MEDIA_SESSION);
+                        notifyListenersOnStateChange(pkg.mUid, pkg.mPackageName, true, now,
+                                STATE_TYPE_MEDIA_SESSION);
                     }
                     // Mark it as active, so we could filter out inactive ones below.
                     mTmpMediaControllers.put(packageName, uid, Boolean.TRUE);
@@ -127,8 +127,8 @@
                                 && mTmpMediaControllers.get(pkg.mPackageName, pkg.mUid) == null) {
                             // This package has removed its controller, issue a stop event.
                             pkg.addEvent(false, now);
-                            notifyListenersOnEvent(pkg.mUid, pkg.mPackageName, false, now,
-                                    EVENT_TYPE_MEDIA_SESSION);
+                            notifyListenersOnStateChange(pkg.mUid, pkg.mPackageName, false, now,
+                                    STATE_TYPE_MEDIA_SESSION);
                         }
                     }
                 }
@@ -146,8 +146,8 @@
                         final SimplePackageDurations pkg = val.valueAt(j);
                         if (pkg.isActive()) {
                             pkg.addEvent(false, now);
-                            notifyListenersOnEvent(pkg.mUid, pkg.mPackageName, false, now,
-                                    EVENT_TYPE_MEDIA_SESSION);
+                            notifyListenersOnStateChange(pkg.mUid, pkg.mPackageName, false, now,
+                                    STATE_TYPE_MEDIA_SESSION);
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java
new file mode 100644
index 0000000..7f48d52
--- /dev/null
+++ b/services/core/java/com/android/server/am/AppPermissionTracker.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.SYSTEM_UID;
+
+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.AppBatteryExemptionTracker.DEFAULT_NAME;
+import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.OnPermissionsChangedListener;
+import android.content.pm.PackageManagerInternal;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.permission.PermissionManager;
+import android.provider.DeviceConfig;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.am.AppPermissionTracker.AppPermissionPolicy;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The tracker for monitoring selected permission state of apps.
+ */
+final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy>
+        implements OnPermissionsChangedListener {
+    static final String TAG = TAG_WITH_CLASS_NAME ? "AppPermissionTracker" : TAG_AM;
+
+    static final boolean DEBUG_PERMISSION_TRACKER = false;
+
+    private final MyHandler mHandler;
+
+    @GuardedBy("mLock")
+    private SparseArray<ArraySet<String>> mUidGrantedPermissionsInMonitor = new SparseArray<>();
+
+    AppPermissionTracker(Context context, AppRestrictionController controller) {
+        this(context, controller, null, null);
+    }
+
+    AppPermissionTracker(Context context, AppRestrictionController controller,
+            Constructor<? extends Injector<AppPermissionPolicy>> injector, Object outerContext) {
+        super(context, controller, injector, outerContext);
+        mHandler = new MyHandler(this);
+        mInjector.setPolicy(new AppPermissionPolicy(mInjector, this));
+    }
+
+    @Override
+    public void onPermissionsChanged(int uid) {
+        mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_CHANGED, uid, 0).sendToTarget();
+    }
+
+    private void handlePermissionsInit() {
+        final int[] allUsers = mInjector.getUserManagerInternal().getUserIds();
+        final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
+        final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
+        final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+        for (int userId : allUsers) {
+            final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, SYSTEM_UID);
+            if (apps == null) {
+                continue;
+            }
+            synchronized (mLock) {
+                final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
+                final long now = SystemClock.elapsedRealtime();
+                for (int i = 0, size = apps.size(); i < size; i++) {
+                    final ApplicationInfo ai = apps.get(i);
+                    for (String permission : permissions) {
+                        if (pm.checkUidPermission(ai.uid, permission) != PERMISSION_GRANTED) {
+                            continue;
+                        }
+                        ArraySet<String> grantedPermissions = uidPerms.get(ai.uid);
+                        if (grantedPermissions == null) {
+                            grantedPermissions = new ArraySet<String>();
+                            uidPerms.put(ai.uid, grantedPermissions);
+                        }
+                        grantedPermissions.add(permission);
+                        notifyListenersOnStateChange(ai.uid, DEFAULT_NAME, true, now,
+                                STATE_TYPE_PERMISSION);
+                    }
+                }
+            }
+        }
+    }
+
+    private void handlePermissionsDestroy() {
+        synchronized (mLock) {
+            final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
+            final long now = SystemClock.elapsedRealtime();
+            for (int i = 0, size = uidPerms.size(); i < size; i++) {
+                final int uid = uidPerms.keyAt(i);
+                final ArraySet<String> grantedPermissions = uidPerms.valueAt(i);
+                for (int j = 0, numOfPerms = grantedPermissions.size(); j < numOfPerms; j++) {
+                    notifyListenersOnStateChange(uid, DEFAULT_NAME, false, now,
+                            STATE_TYPE_PERMISSION);
+                }
+            }
+            uidPerms.clear();
+        }
+    }
+
+    private void handlePermissionsChanged(int uid) {
+        final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+        if (permissions != null && permissions.length > 0) {
+            synchronized (mLock) {
+                handlePermissionsChangedLocked(uid);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void handlePermissionsChangedLocked(int uid) {
+        final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
+        final int index = mUidGrantedPermissionsInMonitor.indexOfKey(uid);
+        ArraySet<String> grantedPermissions = index >= 0
+                ? mUidGrantedPermissionsInMonitor.valueAt(index) : null;
+        final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+        final long now = SystemClock.elapsedRealtime();
+        for (String permission: permissions) {
+            boolean granted = pm.checkUidPermission(uid, permission) == PERMISSION_GRANTED;
+            if (DEBUG_PERMISSION_TRACKER) {
+                Slog.i(TAG, UserHandle.formatUid(uid) + " " + permission + "=" + granted);
+            }
+            boolean changed = false;
+            if (granted) {
+                if (grantedPermissions == null) {
+                    grantedPermissions = new ArraySet<>();
+                    mUidGrantedPermissionsInMonitor.put(uid, grantedPermissions);
+                }
+                changed = grantedPermissions.add(permission);
+            } else if (grantedPermissions != null) {
+                changed = grantedPermissions.remove(permission);
+                if (grantedPermissions.isEmpty()) {
+                    mUidGrantedPermissionsInMonitor.removeAt(index);
+                }
+            }
+            if (changed) {
+                notifyListenersOnStateChange(uid, DEFAULT_NAME, granted, now,
+                        STATE_TYPE_PERMISSION);
+            }
+        }
+    }
+
+    private static class MyHandler extends Handler {
+        static final int MSG_PERMISSIONS_INIT = 0;
+        static final int MSG_PERMISSIONS_DESTROY = 1;
+        static final int MSG_PERMISSIONS_CHANGED = 2;
+
+        private @NonNull AppPermissionTracker mTracker;
+
+        MyHandler(@NonNull AppPermissionTracker tracker) {
+            super(tracker.mBgHandler.getLooper());
+            mTracker = tracker;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_PERMISSIONS_INIT:
+                    mTracker.handlePermissionsInit();
+                    break;
+                case MSG_PERMISSIONS_DESTROY:
+                    mTracker.handlePermissionsDestroy();
+                    break;
+                case MSG_PERMISSIONS_CHANGED:
+                    mTracker.handlePermissionsChanged(msg.arg1);
+                    break;
+            }
+        }
+    }
+
+    private void onPermissionTrackerEnabled(boolean enabled) {
+        final PermissionManager pm = mInjector.getPermissionManager();
+        if (enabled) {
+            pm.addOnPermissionsChangeListener(this);
+            mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_INIT).sendToTarget();
+        } else {
+            pm.removeOnPermissionsChangeListener(this);
+            mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_DESTROY).sendToTarget();
+        }
+    }
+
+    @Override
+    void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix);
+        pw.println("APP PERMISSIONS TRACKER:");
+        final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+        final String prefixMore = "  " + prefix;
+        final String prefixMoreMore = "  " + prefixMore;
+        for (String permission : permissions) {
+            pw.print(prefixMore);
+            pw.print(permission);
+            pw.println(':');
+            synchronized (mLock) {
+                final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
+                pw.print(prefixMoreMore);
+                pw.print('[');
+                boolean needDelimiter = false;
+                for (int i = 0, size = uidPerms.size(); i < size; i++) {
+                    if (uidPerms.valueAt(i).contains(permission)) {
+                        if (needDelimiter) {
+                            pw.print(',');
+                        }
+                        needDelimiter = true;
+                        pw.print(UserHandle.formatUid(uidPerms.keyAt(i)));
+                    }
+                }
+                pw.println(']');
+            }
+        }
+        super.dump(pw, prefix);
+    }
+
+    static final class AppPermissionPolicy extends BaseAppStatePolicy<AppPermissionTracker> {
+        /**
+         * Whether or not we should enable the monitoring on app permissions.
+         */
+        static final String KEY_BG_PERMISSION_MONITOR_ENABLED =
+                DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "permission_monitor_enabled";
+
+        /**
+         * The names of the permissions we're monitoring its changes.
+         */
+        static final String KEY_BG_PERMISSIONS_IN_MONITOR =
+                DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "permission_in_monitor";
+
+        /**
+         * Default value to {@link #mTrackerEnabled}.
+         */
+        static final boolean DEFAULT_BG_PERMISSION_MONITOR_ENABLED = true;
+
+        /**
+         * Default value to {@link #mBgPermissionsInMonitor}.
+         */
+        static final String[] DEFAULT_BG_PERMISSIONS_IN_MONITOR = new String[] {
+            ACCESS_FINE_LOCATION,
+        };
+
+        /**
+         * @see #KEY_BG_PERMISSIONS_IN_MONITOR.
+         */
+        volatile String[] mBgPermissionsInMonitor = DEFAULT_BG_PERMISSIONS_IN_MONITOR;
+
+        AppPermissionPolicy(@NonNull Injector injector, @NonNull AppPermissionTracker tracker) {
+            super(injector, tracker, KEY_BG_PERMISSION_MONITOR_ENABLED,
+                    DEFAULT_BG_PERMISSION_MONITOR_ENABLED);
+        }
+
+        @Override
+        public void onSystemReady() {
+            super.onSystemReady();
+            updateBgPermissionsInMonitor();
+        }
+
+        @Override
+        public void onPropertiesChanged(String name) {
+            switch (name) {
+                case KEY_BG_PERMISSIONS_IN_MONITOR:
+                    updateBgPermissionsInMonitor();
+                    break;
+                default:
+                    super.onPropertiesChanged(name);
+                    break;
+            }
+        }
+
+        String[] getBgPermissionsInMonitor() {
+            return mBgPermissionsInMonitor;
+        }
+
+        private void updateBgPermissionsInMonitor() {
+            final String config = DeviceConfig.getString(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    KEY_BG_PERMISSIONS_IN_MONITOR,
+                    null);
+            mBgPermissionsInMonitor = config != null
+                    ? config.split(",") : DEFAULT_BG_PERMISSIONS_IN_MONITOR;
+        }
+
+        @Override
+        public void onTrackerEnabled(boolean enabled) {
+            mTracker.onPermissionTrackerEnabled(enabled);
+        }
+
+        @Override
+        void dump(PrintWriter pw, String prefix) {
+            pw.print(prefix);
+            pw.println("APP PERMISSION TRACKER POLICY SETTINGS:");
+            prefix = "  " + prefix;
+            super.dump(pw, prefix);
+            pw.print(prefix);
+            pw.print(KEY_BG_PERMISSIONS_IN_MONITOR);
+            pw.print('=');
+            pw.println(Arrays.toString(mBgPermissionsInMonitor));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index a3aa129..1129c19 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -69,6 +69,7 @@
 import static android.os.PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
 import static android.os.PowerExemptionManager.REASON_SYSTEM_MODULE;
 import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
+import static android.os.PowerExemptionManager.reasonCodeToString;
 import static android.os.Process.SYSTEM_UID;
 
 import static com.android.internal.notification.SystemNotificationChannels.ABUSIVE_BACKGROUND_APPS;
@@ -157,6 +158,7 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.function.Consumer;
 
@@ -329,6 +331,8 @@
                         }
                     }
                 }
+                pw.print(" effectiveExemption=");
+                pw.print(reasonCodeToString(getBackgroundRestrictionExemptionReason(mUid)));
             }
 
             String getPackageName() {
@@ -547,17 +551,50 @@
         static final String KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL =
                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "abusive_notification_minimal_interval";
 
+        /**
+         * The behavior for an app with a FGS and its notification is still showing, when the system
+         * detects it's abusive and should be put into bg restricted level. {@code true} - we'll
+         * show the prompt to user, {@code false} - we'll not show it.
+         */
+        static final String KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED =
+                DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "prompt_fgs_with_noti_to_bg_restricted";
+
+        /**
+         * The list of packages to be exempted from all these background restrictions.
+         */
+        static final String KEY_BG_RESTRICTION_EXEMPTED_PACKAGES =
+                DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "restriction_exempted_packages";
+
         static final boolean DEFAULT_BG_AUTO_RESTRICTED_BUCKET_ON_BG_RESTRICTION = false;
         static final long DEFAULT_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL_MS = 24 * 60 * 60 * 1000;
 
+        /**
+         * Default value to {@link #mBgPromptFgsWithNotiToBgRestricted}.
+         */
+        final boolean mDefaultBgPromptFgsWithNotiToBgRestricted;
+
         volatile boolean mBgAutoRestrictedBucket;
 
         volatile boolean mRestrictedBucketEnabled;
 
         volatile long mBgNotificationMinIntervalMs;
 
-        ConstantsObserver(Handler handler) {
+        /**
+         * @see #KEY_BG_RESTRICTION_EXEMPTED_PACKAGES.
+         *
+         *<p> Mutations on them would result in copy-on-write.</p>
+         */
+        volatile Set<String> mBgRestrictionExemptedPackages = Collections.emptySet();
+
+        /**
+         * @see #KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED.
+         */
+        volatile boolean mBgPromptFgsWithNotiToBgRestricted;
+
+        ConstantsObserver(Handler handler, Context context) {
             super(handler);
+            mDefaultBgPromptFgsWithNotiToBgRestricted = context.getResources().getBoolean(
+                    com.android.internal.R.bool.config_bg_prompt_fgs_with_noti_to_bg_restricted);
         }
 
         @Override
@@ -573,6 +610,12 @@
                     case KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL:
                         updateBgAbusiveNotificationMinimalInterval();
                         break;
+                    case KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED:
+                        updateBgPromptFgsWithNotiToBgRestricted();
+                        break;
+                    case KEY_BG_RESTRICTION_EXEMPTED_PACKAGES:
+                        updateBgRestrictionExemptedPackages();
+                        break;
                 }
                 AppRestrictionController.this.onPropertiesChanged(name);
             }
@@ -604,6 +647,8 @@
         void updateDeviceConfig() {
             updateBgAutoRestrictedBucketChanged();
             updateBgAbusiveNotificationMinimalInterval();
+            updateBgPromptFgsWithNotiToBgRestricted();
+            updateBgRestrictionExemptedPackages();
         }
 
         private void updateBgAutoRestrictedBucketChanged() {
@@ -623,6 +668,53 @@
                     KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL,
                     DEFAULT_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL_MS);
         }
+
+        private void updateBgPromptFgsWithNotiToBgRestricted() {
+            mBgPromptFgsWithNotiToBgRestricted = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED,
+                    mDefaultBgPromptFgsWithNotiToBgRestricted);
+        }
+
+        private void updateBgRestrictionExemptedPackages() {
+            final String settings = DeviceConfig.getString(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    KEY_BG_RESTRICTION_EXEMPTED_PACKAGES,
+                    null);
+            if (settings == null) {
+                mBgRestrictionExemptedPackages = Collections.emptySet();
+                return;
+            }
+            final String[] settingsList = settings.split(",");
+            final ArraySet<String> packages = new ArraySet<>();
+            for (String pkg : settingsList) {
+                packages.add(pkg);
+            }
+            mBgRestrictionExemptedPackages = Collections.unmodifiableSet(packages);
+        }
+
+        void dump(PrintWriter pw, String prefix) {
+            pw.print(prefix);
+            pw.println("BACKGROUND RESTRICTION POLICY SETTINGS:");
+            final String indent = "  ";
+            prefix = indent + prefix;
+            pw.print(prefix);
+            pw.print(KEY_BG_AUTO_RESTRICTED_BUCKET_ON_BG_RESTRICTION);
+            pw.print('=');
+            pw.println(mBgAutoRestrictedBucket);
+            pw.print(prefix);
+            pw.print(KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL);
+            pw.print('=');
+            pw.println(mBgNotificationMinIntervalMs);
+            pw.print(prefix);
+            pw.print(KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED);
+            pw.print('=');
+            pw.println(mBgPromptFgsWithNotiToBgRestricted);
+            pw.print(prefix);
+            pw.print(KEY_BG_RESTRICTION_EXEMPTED_PACKAGES);
+            pw.print('=');
+            pw.println(mBgRestrictionExemptedPackages.toString());
+        }
     }
 
     private final ConstantsObserver mConstantsObserver;
@@ -704,7 +796,7 @@
         mBgHandlerThread.start();
         mBgHandler = new BgHandler(mBgHandlerThread.getLooper(), injector);
         mBgExecutor = new HandlerExecutor(mBgHandler);
-        mConstantsObserver = new ConstantsObserver(mBgHandler);
+        mConstantsObserver = new ConstantsObserver(mBgHandler, mContext);
         mNotificationHelper = new NotificationHelper(this);
         injector.initAppStateTrackers(this);
     }
@@ -1074,12 +1166,27 @@
     }
 
     /**
+     * @return If the given package/uid has a foreground service notification or not.
+     */
+    boolean hasForegroundServiceNotifications(String packageName, int uid) {
+        return mInjector.getAppFGSTracker().hasForegroundServiceNotifications(packageName, uid);
+    }
+
+    /**
+     * @return If the given uid has a foreground service notification or not.
+     */
+    boolean hasForegroundServiceNotifications(int uid) {
+        return mInjector.getAppFGSTracker().hasForegroundServiceNotifications(uid);
+    }
+
+    /**
      * @return The to-be-exempted battery usage of the given UID in the given duration; it could
      *         be considered as "exempted" due to various use cases, i.e. media playback.
      */
-    ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now) {
+    ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now,
+            int types) {
         return mInjector.getAppBatteryExemptionTracker()
-                .getUidBatteryExemptedUsageSince(uid, since, now);
+                .getUidBatteryExemptedUsageSince(uid, since, now, types);
     }
 
     /**
@@ -1098,11 +1205,14 @@
 
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix);
-        pw.println("BACKGROUND RESTRICTION LEVEL SETTINGS");
+        pw.println("APP BACKGROUND RESTRICTIONS");
         prefix = "  " + prefix;
+        pw.print(prefix);
+        pw.println("BACKGROUND RESTRICTION LEVEL SETTINGS");
         synchronized (mLock) {
-            mRestrictionSettings.dumpLocked(pw, prefix);
+            mRestrictionSettings.dumpLocked(pw, "  " + prefix);
         }
+        mConstantsObserver.dump(pw, "  " + prefix);
         for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
             pw.println();
             mAppStateTrackers.get(i).dump(pw, prefix);
@@ -1366,8 +1476,21 @@
                     intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, null,
                     UserHandle.of(UserHandle.getUserId(uid)));
             Notification.Action[] actions = null;
-            if (ENABLE_SHOW_FOREGROUND_SERVICE_MANAGER
-                    && mBgController.hasForegroundServices(packageName, uid)) {
+            final boolean hasForegroundServices =
+                    mBgController.hasForegroundServices(packageName, uid);
+            final boolean hasForegroundServiceNotifications =
+                    mBgController.hasForegroundServiceNotifications(packageName, uid);
+            if (!mBgController.mConstantsObserver.mBgPromptFgsWithNotiToBgRestricted) {
+                // We're not going to prompt the user if the FGS is active and its notification
+                // is still showing (not dismissed/silenced/denied).
+                if (hasForegroundServices && hasForegroundServiceNotifications) {
+                    if (DEBUG_BG_RESTRICTION_CONTROLLER) {
+                        Slog.i(TAG, "Not requesting bg-restriction due to FGS with notification");
+                    }
+                    return;
+                }
+            }
+            if (ENABLE_SHOW_FOREGROUND_SERVICE_MANAGER && hasForegroundServices) {
                 final Intent trampoline = new Intent(ACTION_FGS_MANAGER_TRAMPOLINE);
                 trampoline.setPackage("android");
                 trampoline.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
@@ -1646,6 +1769,8 @@
                     return REASON_CARRIER_PRIVILEGED_APP;
                 } else if (isExemptedFromSysConfig(pkg)) {
                     return REASON_SYSTEM_ALLOW_LISTED;
+                } else if (mConstantsObserver.mBgRestrictionExemptedPackages.contains(pkg)) {
+                    return REASON_ALLOWLISTED_PACKAGE;
                 }
             }
         }
@@ -1864,6 +1989,7 @@
         private AppBatteryExemptionTracker mAppBatteryExemptionTracker;
         private AppFGSTracker mAppFGSTracker;
         private AppMediaSessionTracker mAppMediaSessionTracker;
+        private AppPermissionTracker mAppPermissionTracker;
         private TelephonyManager mTelephonyManager;
 
         Injector(Context context) {
@@ -1880,10 +2006,12 @@
             mAppBatteryExemptionTracker = new AppBatteryExemptionTracker(mContext, controller);
             mAppFGSTracker = new AppFGSTracker(mContext, controller);
             mAppMediaSessionTracker = new AppMediaSessionTracker(mContext, controller);
+            mAppPermissionTracker = new AppPermissionTracker(mContext, controller);
             controller.mAppStateTrackers.add(mAppBatteryTracker);
             controller.mAppStateTrackers.add(mAppBatteryExemptionTracker);
             controller.mAppStateTrackers.add(mAppFGSTracker);
             controller.mAppStateTrackers.add(mAppMediaSessionTracker);
+            controller.mAppStateTrackers.add(mAppPermissionTracker);
             controller.mAppStateTrackers.add(new AppBroadcastEventsTracker(mContext, controller));
             controller.mAppStateTrackers.add(new AppBindServiceEventsTracker(mContext, controller));
         }
@@ -1991,6 +2119,10 @@
             return mAppBatteryExemptionTracker;
         }
 
+        AppPermissionTracker getAppPermissionTracker() {
+            return mAppPermissionTracker;
+        }
+
         String getPackageName(int pid) {
             final ActivityManagerService am = getActivityManagerService();
             final ProcessRecord app;
diff --git a/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java b/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java
index cc89e84..80a219d4 100644
--- a/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java
@@ -19,7 +19,6 @@
 import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 
-import android.annotation.NonNull;
 import android.content.Context;
 import android.os.SystemClock;
 import android.util.SparseArray;
@@ -32,7 +31,6 @@
 
 import java.io.PrintWriter;
 import java.lang.reflect.Constructor;
-import java.util.ArrayList;
 import java.util.LinkedList;
 
 /**
@@ -43,20 +41,9 @@
         extends BaseAppStateEventsTracker<T, U> {
     static final boolean DEBUG_BASE_APP_STATE_DURATION_TRACKER = false;
 
-    static final int EVENT_TYPE_MEDIA_SESSION = 0;
-    static final int EVENT_TYPE_FGS_MEDIA_PLAYBACK = 1;
-    static final int EVENT_TYPE_FGS_LOCATION = 2;
-    static final int EVENT_NUM = 3;
-
-    final ArrayList<EventListener> mEventListeners = new ArrayList<>();
-
     @GuardedBy("mLock")
     final SparseArray<UidStateDurations> mUidStateDurations = new SparseArray<>();
 
-    interface EventListener {
-        void onNewEvent(int uid, String packageName, boolean start, long now, int eventType);
-    }
-
     BaseAppStateDurationsTracker(Context context, AppRestrictionController controller,
             Constructor<? extends Injector<T>> injector, Object outerContext) {
         super(context, controller, injector, outerContext);
@@ -104,21 +91,6 @@
         mUidStateDurations.remove(uid);
     }
 
-    void registerEventListener(@NonNull EventListener listener) {
-        synchronized (mLock) {
-            mEventListeners.add(listener);
-        }
-    }
-
-    void notifyListenersOnEvent(int uid, String packageName,
-            boolean start, long now, int eventType) {
-        synchronized (mLock) {
-            for (int i = 0, size = mEventListeners.size(); i < size; i++) {
-                mEventListeners.get(i).onNewEvent(uid, packageName, start, now, eventType);
-            }
-        }
-    }
-
     long getTotalDurations(String packageName, int uid, long now, int index, boolean bgOnly) {
         synchronized (mLock) {
             final U durations = mPkgEvents.get(uid, packageName);
diff --git a/services/core/java/com/android/server/am/BaseAppStateEventsTracker.java b/services/core/java/com/android/server/am/BaseAppStateEventsTracker.java
index 3e1bcae..c6900b2 100644
--- a/services/core/java/com/android/server/am/BaseAppStateEventsTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateEventsTracker.java
@@ -184,9 +184,13 @@
                 }
             }
         }
+        dumpOthers(pw, prefix);
         policy.dump(pw, prefix);
     }
 
+    void dumpOthers(PrintWriter pw, String prefix) {
+    }
+
     @GuardedBy("mLock")
     void dumpEventHeaderLocked(PrintWriter pw, String prefix, String packageName, int uid, U events,
             T policy) {
diff --git a/services/core/java/com/android/server/am/BaseAppStateTracker.java b/services/core/java/com/android/server/am/BaseAppStateTracker.java
index 2846f6c..482d697 100644
--- a/services/core/java/com/android/server/am/BaseAppStateTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateTracker.java
@@ -28,19 +28,23 @@
 import android.app.role.RoleManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.media.session.MediaSessionManager;
 import android.os.BatteryManagerInternal;
 import android.os.BatteryStatsInternal;
 import android.os.Handler;
+import android.permission.PermissionManager;
 import android.util.Slog;
 
 import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
+import com.android.server.notification.NotificationManagerInternal;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 import java.io.PrintWriter;
 import java.lang.reflect.Constructor;
+import java.util.ArrayList;
 
 /**
  * Base class to track certain state of the app, could be used to determine the restriction level.
@@ -54,11 +58,27 @@
     static final long ONE_HOUR = 60 * ONE_MINUTE;
     static final long ONE_DAY = 24 * ONE_HOUR;
 
+    static final int STATE_TYPE_MEDIA_SESSION = 1;
+    static final int STATE_TYPE_FGS_MEDIA_PLAYBACK = 1 << 1;
+    static final int STATE_TYPE_FGS_LOCATION = 1 << 2;
+    static final int STATE_TYPE_PERMISSION = 1 << 3;
+    static final int STATE_TYPE_NUM = 4;
+
+    static final int STATE_TYPE_INDEX_MEDIA_SESSION = 0;
+    static final int STATE_TYPE_INDEX_FGS_MEDIA_PLAYBACK = 1;
+    static final int STATE_TYPE_INDEX_FGS_LOCATION = 2;
+    static final int STATE_TYPE_INDEX_PERMISSION = 3;
+
     protected final AppRestrictionController mAppRestrictionController;
     protected final Injector<T> mInjector;
     protected final Context mContext;
     protected final Handler mBgHandler;
     protected final Object mLock;
+    protected final ArrayList<StateListener> mStateListeners = new ArrayList<>();
+
+    interface StateListener {
+        void onStateChange(int uid, String packageName, boolean start, long now, int stateType);
+    }
 
     BaseAppStateTracker(Context context, AppRestrictionController controller,
             @Nullable Constructor<? extends Injector<T>> injector, Object outerContext) {
@@ -79,6 +99,60 @@
         }
     }
 
+    static int stateTypeToIndex(int stateType) {
+        return Integer.numberOfTrailingZeros(stateType);
+    }
+
+    static int stateIndexToType(int stateTypeIndex) {
+        return 1 << stateTypeIndex;
+    }
+
+    static String stateTypesToString(int stateTypes) {
+        final StringBuilder sb = new StringBuilder("[");
+        boolean needDelimiter = false;
+        for (int stateType = Integer.highestOneBit(stateTypes); stateType != 0;
+                stateType = Integer.highestOneBit(stateTypes)) {
+            if (needDelimiter) {
+                sb.append('|');
+            }
+            needDelimiter = true;
+            switch (stateType) {
+                case STATE_TYPE_MEDIA_SESSION:
+                    sb.append("MEDIA_SESSION");
+                    break;
+                case STATE_TYPE_FGS_MEDIA_PLAYBACK:
+                    sb.append("FGS_MEDIA_PLAYBACK");
+                    break;
+                case STATE_TYPE_FGS_LOCATION:
+                    sb.append("FGS_LOCATION");
+                    break;
+                case STATE_TYPE_PERMISSION:
+                    sb.append("PERMISSION");
+                    break;
+                default:
+                    return "[UNKNOWN(" + Integer.toHexString(stateTypes) + ")]";
+            }
+            stateTypes &= ~stateType;
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    void registerStateListener(@NonNull StateListener listener) {
+        synchronized (mLock) {
+            mStateListeners.add(listener);
+        }
+    }
+
+    void notifyListenersOnStateChange(int uid, String packageName,
+            boolean start, long now, int stateType) {
+        synchronized (mLock) {
+            for (int i = 0, size = mStateListeners.size(); i < size; i++) {
+                mStateListeners.get(i).onStateChange(uid, packageName, start, now, stateType);
+            }
+        }
+    }
+
     /**
      * Return the policy holder of this tracker.
      */
@@ -179,10 +253,13 @@
         DeviceIdleInternal mDeviceIdleInternal;
         UserManagerInternal mUserManagerInternal;
         PackageManager mPackageManager;
+        PackageManagerInternal mPackageManagerInternal;
+        PermissionManager mPermissionManager;
         PermissionManagerServiceInternal mPermissionManagerServiceInternal;
         AppOpsManager mAppOpsManager;
         MediaSessionManager mMediaSessionManager;
         RoleManager mRoleManager;
+        NotificationManagerInternal mNotificationManagerInternal;
 
         void setPolicy(T policy) {
             mAppStatePolicy = policy;
@@ -194,13 +271,17 @@
             mBatteryStatsInternal = LocalServices.getService(BatteryStatsInternal.class);
             mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class);
             mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+            mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
             mPermissionManagerServiceInternal = LocalServices.getService(
                     PermissionManagerServiceInternal.class);
             final Context context = mAppStatePolicy.mTracker.mContext;
             mPackageManager = context.getPackageManager();
             mAppOpsManager = context.getSystemService(AppOpsManager.class);
             mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
+            mPermissionManager = context.getSystemService(PermissionManager.class);
             mRoleManager = context.getSystemService(RoleManager.class);
+            mNotificationManagerInternal = LocalServices.getService(
+                    NotificationManagerInternal.class);
 
             getPolicy().onSystemReady();
         }
@@ -240,6 +321,14 @@
             return mPackageManager;
         }
 
+        PackageManagerInternal getPackageManagerInternal() {
+            return mPackageManagerInternal;
+        }
+
+        PermissionManager getPermissionManager() {
+            return mPermissionManager;
+        }
+
         PermissionManagerServiceInternal getPermissionManagerServiceInternal() {
             return mPermissionManagerServiceInternal;
         }
@@ -259,5 +348,9 @@
         RoleManager getRoleManager() {
             return mRoleManager;
         }
+
+        NotificationManagerInternal getNotificationManagerInternal() {
+            return mNotificationManagerInternal;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index e580327..2ebe0b4 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -16,6 +16,10 @@
 
 package com.android.server.am;
 
+import android.annotation.IntDef;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Overridable;
 import android.content.ContentResolver;
 import android.database.ContentObserver;
 import android.os.Build;
@@ -26,6 +30,8 @@
 import android.util.TimeUtils;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Tunable parameters for broadcast dispatch policy
@@ -51,6 +57,47 @@
     private static final long DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT =
             10_000 * Build.HW_TIMEOUT_MULTIPLIER;
 
+    /**
+     * Defer LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts until the first time any process in
+     * the UID is started.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.S_V2)
+    @Overridable
+    static final long DEFER_BOOT_COMPLETED_BROADCAST_CHANGE_ID = 203704822L;
+
+    /**
+     * Do not defer LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts.
+     */
+    public static final int DEFER_BOOT_COMPLETED_BROADCAST_NONE = 0;
+    /**
+     * Defer all LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts.
+     */
+    public static final int DEFER_BOOT_COMPLETED_BROADCAST_ALL = 1 << 0;
+    /**
+     * Defer LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts if app is background restricted.
+     */
+    public static final int DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY = 1 << 1;
+    /**
+     * Defer LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts if app's targetSdkVersion is T
+     * and above.
+     */
+    public static final int DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY = 1 << 2;
+
+    /**
+     * The list of DEFER_BOOT_COMPLETED_BROADCAST types.
+     * If multiple flags are selected, all conditions must be met to defer the broadcast.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "DEFER_BOOT_COMPLETED_BROADCAST_" }, value = {
+            DEFER_BOOT_COMPLETED_BROADCAST_NONE,
+            DEFER_BOOT_COMPLETED_BROADCAST_ALL,
+            DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY,
+            DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeferBootCompletedBroadcastType {}
+
     // All time constants are in milliseconds
 
     // Timeout period for this broadcast queue
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
index 8afd52e..01d8109 100644
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -18,14 +18,21 @@
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
 
+import android.annotation.Nullable;
 import android.content.Intent;
+import android.content.pm.ResolveInfo;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.AlarmManagerInternal;
 import com.android.server.LocalServices;
 
@@ -232,6 +239,253 @@
     // Next outbound broadcast, established by getNextBroadcastLocked()
     private BroadcastRecord mCurrentBroadcast;
 
+    // Map userId to its deferred boot completed broadcasts.
+    private SparseArray<DeferredBootCompletedBroadcastPerUser> mUser2Deferred = new SparseArray<>();
+
+    /**
+     * Deferred LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts that is sent to a user.
+     */
+    static class DeferredBootCompletedBroadcastPerUser {
+        private int mUserId;
+        // UID that has process started at least once, ready to execute LOCKED_BOOT_COMPLETED
+        // receivers.
+        @VisibleForTesting
+        SparseBooleanArray mUidReadyForLockedBootCompletedBroadcast = new SparseBooleanArray();
+        // UID that has process started at least once, ready to execute BOOT_COMPLETED receivers.
+        @VisibleForTesting
+        SparseBooleanArray mUidReadyForBootCompletedBroadcast = new SparseBooleanArray();
+        // Map UID to deferred LOCKED_BOOT_COMPLETED broadcasts.
+        // LOCKED_BOOT_COMPLETED broadcast receivers are deferred until the first time the uid has
+        // any process started.
+        @VisibleForTesting
+        SparseArray<BroadcastRecord> mDeferredLockedBootCompletedBroadcasts = new SparseArray<>();
+        // is the LOCKED_BOOT_COMPLETED broadcast received by the user.
+        @VisibleForTesting
+        boolean mLockedBootCompletedBroadcastReceived;
+        // Map UID to deferred BOOT_COMPLETED broadcasts.
+        // BOOT_COMPLETED broadcast receivers are deferred until the first time the uid has any
+        // process started.
+        @VisibleForTesting
+        SparseArray<BroadcastRecord> mDeferredBootCompletedBroadcasts = new SparseArray<>();
+        // is the BOOT_COMPLETED broadcast received by the user.
+        @VisibleForTesting
+        boolean mBootCompletedBroadcastReceived;
+
+        DeferredBootCompletedBroadcastPerUser(int userId) {
+            this.mUserId = userId;
+        }
+
+        public void updateUidReady(int uid) {
+            if (!mLockedBootCompletedBroadcastReceived
+                    || mDeferredLockedBootCompletedBroadcasts.size() != 0) {
+                mUidReadyForLockedBootCompletedBroadcast.put(uid, true);
+            }
+            if (!mBootCompletedBroadcastReceived
+                    || mDeferredBootCompletedBroadcasts.size() != 0) {
+                mUidReadyForBootCompletedBroadcast.put(uid, true);
+            }
+        }
+
+        public void enqueueBootCompletedBroadcasts(String action,
+                SparseArray<BroadcastRecord> deferred) {
+            if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
+                enqueueBootCompletedBroadcasts(deferred, mDeferredLockedBootCompletedBroadcasts,
+                        mUidReadyForLockedBootCompletedBroadcast);
+                mLockedBootCompletedBroadcastReceived = true;
+                if (DEBUG_BROADCAST_DEFERRAL) {
+                    dumpBootCompletedBroadcastRecord(mDeferredLockedBootCompletedBroadcasts);
+                }
+            } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+                enqueueBootCompletedBroadcasts(deferred, mDeferredBootCompletedBroadcasts,
+                        mUidReadyForBootCompletedBroadcast);
+                mBootCompletedBroadcastReceived = true;
+                if (DEBUG_BROADCAST_DEFERRAL) {
+                    dumpBootCompletedBroadcastRecord(mDeferredBootCompletedBroadcasts);
+                }
+            }
+        }
+
+        /**
+         * Merge UID to BroadcastRecord map into {@link #mDeferredBootCompletedBroadcasts} or
+         * {@link #mDeferredLockedBootCompletedBroadcasts}
+         * @param from the UID to BroadcastRecord map.
+         * @param into The UID to list of BroadcastRecord map.
+         */
+        private void enqueueBootCompletedBroadcasts(SparseArray<BroadcastRecord> from,
+                SparseArray<BroadcastRecord> into, SparseBooleanArray uidReadyForReceiver) {
+            // remove unwanted uids from uidReadyForReceiver.
+            for (int i = uidReadyForReceiver.size() - 1; i >= 0; i--) {
+                if (from.indexOfKey(uidReadyForReceiver.keyAt(i)) < 0) {
+                    uidReadyForReceiver.removeAt(i);
+                }
+            }
+            for (int i = 0, size = from.size(); i < size; i++) {
+                final int uid = from.keyAt(i);
+                into.put(uid, from.valueAt(i));
+                if (uidReadyForReceiver.indexOfKey(uid) < 0) {
+                    // uid is wanted but not ready.
+                    uidReadyForReceiver.put(uid, false);
+                }
+            }
+        }
+
+        public @Nullable BroadcastRecord dequeueDeferredBootCompletedBroadcast(
+                boolean isAllUidReady) {
+            BroadcastRecord next = dequeueDeferredBootCompletedBroadcast(
+                    mDeferredLockedBootCompletedBroadcasts,
+                    mUidReadyForLockedBootCompletedBroadcast, isAllUidReady);
+            if (next == null) {
+                next = dequeueDeferredBootCompletedBroadcast(mDeferredBootCompletedBroadcasts,
+                        mUidReadyForBootCompletedBroadcast, isAllUidReady);
+            }
+            return next;
+        }
+
+        private @Nullable BroadcastRecord dequeueDeferredBootCompletedBroadcast(
+                SparseArray<BroadcastRecord> uid2br, SparseBooleanArray uidReadyForReceiver,
+                boolean isAllUidReady) {
+            for (int i = 0, size = uid2br.size(); i < size; i++) {
+                final int uid = uid2br.keyAt(i);
+                if (isAllUidReady || uidReadyForReceiver.get(uid)) {
+                    final BroadcastRecord br = uid2br.valueAt(i);
+                    if (DEBUG_BROADCAST_DEFERRAL) {
+                        final Object receiver = br.receivers.get(0);
+                        if (receiver instanceof BroadcastFilter) {
+                            if (DEBUG_BROADCAST_DEFERRAL) {
+                                Slog.i(TAG, "getDeferredBootCompletedBroadcast uid:" + uid
+                                        + " BroadcastFilter:" + (BroadcastFilter) receiver
+                                        + " broadcast:" + br.intent.getAction());
+                            }
+                        } else /* if (receiver instanceof ResolveInfo) */ {
+                            ResolveInfo info = (ResolveInfo) receiver;
+                            String packageName = info.activityInfo.applicationInfo.packageName;
+                            if (DEBUG_BROADCAST_DEFERRAL) {
+                                Slog.i(TAG, "getDeferredBootCompletedBroadcast uid:" + uid
+                                        + " packageName:" + packageName
+                                        + " broadcast:" + br.intent.getAction());
+                            }
+                        }
+                    }
+                    // remove the BroadcastRecord.
+                    uid2br.removeAt(i);
+                    if (uid2br.size() == 0) {
+                        // All deferred receivers are executed, do not need uidReadyForReceiver
+                        // any more.
+                        uidReadyForReceiver.clear();
+                    }
+                    return br;
+                }
+            }
+            return null;
+        }
+
+        private @Nullable SparseArray<BroadcastRecord> getDeferredList(String action) {
+            SparseArray<BroadcastRecord> brs = null;
+            if (action.equals(Intent.ACTION_LOCKED_BOOT_COMPLETED)) {
+                brs = mDeferredLockedBootCompletedBroadcasts;
+            } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+                brs = mDeferredBootCompletedBroadcasts;
+            }
+            return brs;
+        }
+
+        /**
+         * Return the total number of UIDs in all BroadcastRecord in
+         * {@link #mDeferredBootCompletedBroadcasts} or
+         * {@link #mDeferredLockedBootCompletedBroadcasts}
+         */
+        private int getBootCompletedBroadcastsUidsSize(String action) {
+            SparseArray<BroadcastRecord> brs = getDeferredList(action);
+            return brs != null ? brs.size() : 0;
+        }
+
+        /**
+         * Return the total number of receivers in all BroadcastRecord in
+         * {@link #mDeferredBootCompletedBroadcasts} or
+         * {@link #mDeferredLockedBootCompletedBroadcasts}
+         */
+        private int getBootCompletedBroadcastsReceiversSize(String action) {
+            SparseArray<BroadcastRecord> brs = getDeferredList(action);
+            if (brs == null) {
+                return 0;
+            }
+            int size = 0;
+            for (int i = 0, s = brs.size(); i < s; i++) {
+                size += brs.valueAt(i).receivers.size();
+            }
+            return size;
+        }
+
+        public void dump(Dumper dumper, String action) {
+            SparseArray<BroadcastRecord> brs = getDeferredList(action);
+            if (brs == null) {
+                return;
+            }
+            for (int i = 0, size = brs.size(); i < size; i++) {
+                dumper.dump(brs.valueAt(i));
+            }
+        }
+
+        public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+            for (int i = 0, size = mDeferredLockedBootCompletedBroadcasts.size(); i < size; i++) {
+                mDeferredLockedBootCompletedBroadcasts.valueAt(i).dumpDebug(proto, fieldId);
+            }
+            for (int i = 0, size = mDeferredBootCompletedBroadcasts.size(); i < size; i++) {
+                mDeferredBootCompletedBroadcasts.valueAt(i).dumpDebug(proto, fieldId);
+            }
+        }
+
+        private void dumpBootCompletedBroadcastRecord(SparseArray<BroadcastRecord> brs) {
+            for (int i = 0, size = brs.size(); i < size; i++) {
+                final Object receiver = brs.valueAt(i).receivers.get(0);
+                String packageName = null;
+                if (receiver instanceof BroadcastFilter) {
+                    BroadcastFilter recv = (BroadcastFilter) receiver;
+                    packageName = recv.receiverList.app.processName;
+                } else /* if (receiver instanceof ResolveInfo) */ {
+                    ResolveInfo info = (ResolveInfo) receiver;
+                    packageName = info.activityInfo.applicationInfo.packageName;
+                }
+                Slog.i(TAG, "uid:" + brs.keyAt(i)
+                        + " packageName:" + packageName
+                        + " receivers:" + brs.valueAt(i).receivers.size());
+            }
+        }
+    }
+
+    private DeferredBootCompletedBroadcastPerUser getDeferredPerUser(int userId) {
+        if (mUser2Deferred.contains(userId)) {
+            return mUser2Deferred.get(userId);
+        } else {
+            final DeferredBootCompletedBroadcastPerUser temp =
+                    new DeferredBootCompletedBroadcastPerUser(userId);
+            mUser2Deferred.put(userId, temp);
+            return temp;
+        }
+    }
+
+    /**
+     * ActivityManagerService.attachApplication() call this method to notify that the UID is ready
+     * to accept deferred LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts.
+     * @param uid
+     */
+    public void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
+        getDeferredPerUser(UserHandle.getUserId(uid)).updateUidReady(uid);
+    }
+
+    private @Nullable BroadcastRecord dequeueDeferredBootCompletedBroadcast() {
+        final boolean isAllUidReady = (mQueue.mService.mConstants.mDeferBootCompletedBroadcast
+                == DEFER_BOOT_COMPLETED_BROADCAST_NONE);
+        BroadcastRecord next = null;
+        for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
+            next = mUser2Deferred.valueAt(i).dequeueDeferredBootCompletedBroadcast(isAllUidReady);
+            if (next != null) {
+                break;
+            }
+        }
+        return next;
+    }
+
     /**
      * Constructed & sharing a lock with its associated BroadcastQueue instance
      */
@@ -261,7 +515,9 @@
             return mCurrentBroadcast == null
                     && mOrderedBroadcasts.isEmpty()
                     && isDeferralsListEmpty(mDeferredBroadcasts)
-                    && isDeferralsListEmpty(mAlarmBroadcasts);
+                    && isDeferralsListEmpty(mAlarmBroadcasts)
+                    && getBootCompletedBroadcastsUidsSize(Intent.ACTION_LOCKED_BOOT_COMPLETED) == 0
+                    && getBootCompletedBroadcastsUidsSize(Intent.ACTION_BOOT_COMPLETED) == 0;
         }
     }
 
@@ -301,14 +557,78 @@
             sb.append(n);
             sb.append(" deferred");
         }
+        n = getBootCompletedBroadcastsUidsSize(Intent.ACTION_LOCKED_BOOT_COMPLETED);
+        if (n > 0) {
+            sb.append(", ");
+            sb.append(n);
+            sb.append(" deferred LOCKED_BOOT_COMPLETED/");
+            sb.append(getBootCompletedBroadcastsReceiversSize(Intent.ACTION_LOCKED_BOOT_COMPLETED));
+            sb.append(" receivers");
+        }
+
+        n = getBootCompletedBroadcastsUidsSize(Intent.ACTION_BOOT_COMPLETED);
+        if (n > 0) {
+            sb.append(", ");
+            sb.append(n);
+            sb.append(" deferred BOOT_COMPLETED/");
+            sb.append(getBootCompletedBroadcastsReceiversSize(Intent.ACTION_BOOT_COMPLETED));
+            sb.append(" receivers");
+        }
         return sb.toString();
     }
 
     // ----------------------------------
     // BroadcastQueue operation support
-
     void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
-        mOrderedBroadcasts.add(r);
+        if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(r.intent.getAction())) {
+            // Create one BroadcastRecord for each UID that can be deferred.
+            final SparseArray<BroadcastRecord> deferred =
+                    r.splitDeferredBootCompletedBroadcastLocked(mQueue.mService.mInternal,
+                            mQueue.mService.mConstants.mDeferBootCompletedBroadcast);
+            getDeferredPerUser(r.userId).enqueueBootCompletedBroadcasts(
+                    Intent.ACTION_LOCKED_BOOT_COMPLETED, deferred);
+            if (!r.receivers.isEmpty()) {
+                // The non-deferred receivers.
+                mOrderedBroadcasts.add(r);
+                return;
+            }
+        } else if (Intent.ACTION_BOOT_COMPLETED.equals(r.intent.getAction())) {
+            // Create one BroadcastRecord for each UID that can be deferred.
+            final SparseArray<BroadcastRecord> deferred =
+                    r.splitDeferredBootCompletedBroadcastLocked(mQueue.mService.mInternal,
+                            mQueue.mService.mConstants.mDeferBootCompletedBroadcast);
+            getDeferredPerUser(r.userId).enqueueBootCompletedBroadcasts(
+                    Intent.ACTION_BOOT_COMPLETED, deferred);
+            if (!r.receivers.isEmpty()) {
+                // The non-deferred receivers.
+                mOrderedBroadcasts.add(r);
+                return;
+            }
+        } else {
+            mOrderedBroadcasts.add(r);
+        }
+    }
+
+    /**
+     * Return the total number of UIDs in all deferred boot completed BroadcastRecord.
+     */
+    private int getBootCompletedBroadcastsUidsSize(String action) {
+        int size = 0;
+        for (int i = 0, s = mUser2Deferred.size(); i < s; i++) {
+            size += mUser2Deferred.valueAt(i).getBootCompletedBroadcastsUidsSize(action);
+        }
+        return size;
+    }
+
+    /**
+     * Return the total number of receivers in all deferred boot completed BroadcastRecord.
+     */
+    private int getBootCompletedBroadcastsReceiversSize(String action) {
+        int size = 0;
+        for (int i = 0, s = mUser2Deferred.size(); i < s; i++) {
+            size += mUser2Deferred.valueAt(i).getBootCompletedBroadcastsReceiversSize(action);
+        }
+        return size;
     }
 
     // Returns the now-replaced broadcast record, or null if none
@@ -369,6 +689,31 @@
         boolean didSomething = cleanupBroadcastListDisabledReceiversLocked(mOrderedBroadcasts,
                 packageName, filterByClasses, userId, doit);
         if (doit || !didSomething) {
+            ArrayList<BroadcastRecord> lockedBootCompletedBroadcasts = new ArrayList<>();
+            for (int u = 0, usize = mUser2Deferred.size(); u < usize; u++) {
+                SparseArray<BroadcastRecord> brs =
+                        mUser2Deferred.valueAt(u).mDeferredLockedBootCompletedBroadcasts;
+                for (int i = 0, size = brs.size(); i < size; i++) {
+                    lockedBootCompletedBroadcasts.add(brs.valueAt(i));
+                }
+            }
+            didSomething = cleanupBroadcastListDisabledReceiversLocked(
+                    lockedBootCompletedBroadcasts,
+                    packageName, filterByClasses, userId, doit);
+        }
+        if (doit || !didSomething) {
+            ArrayList<BroadcastRecord> bootCompletedBroadcasts = new ArrayList<>();
+            for (int u = 0, usize = mUser2Deferred.size(); u < usize; u++) {
+                SparseArray<BroadcastRecord> brs =
+                        mUser2Deferred.valueAt(u).mDeferredBootCompletedBroadcasts;
+                for (int i = 0, size = brs.size(); i < size; i++) {
+                    bootCompletedBroadcasts.add(brs.valueAt(i));
+                }
+            }
+            didSomething = cleanupBroadcastListDisabledReceiversLocked(bootCompletedBroadcasts,
+                    packageName, filterByClasses, userId, doit);
+        }
+        if (doit || !didSomething) {
             didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmBroadcasts,
                     packageName, filterByClasses, userId, doit);
         }
@@ -428,6 +773,10 @@
         for (Deferrals d : mDeferredBroadcasts) {
             d.dumpDebug(proto, fieldId);
         }
+
+        for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
+            mUser2Deferred.valueAt(i).dumpDebug(proto, fieldId);
+        }
     }
 
     // ----------------------------------
@@ -453,7 +802,12 @@
         final boolean someQueued = !mOrderedBroadcasts.isEmpty();
 
         BroadcastRecord next = null;
-        if (!mAlarmBroadcasts.isEmpty()) {
+
+        if (next == null) {
+            next = dequeueDeferredBootCompletedBroadcast();
+        }
+
+        if (next == null && !mAlarmBroadcasts.isEmpty()) {
             next = popLocked(mAlarmBroadcasts);
             if (DEBUG_BROADCAST_DEFERRAL && next != null) {
                 Slog.i(TAG, "Next broadcast from alarm targets: " + next);
@@ -752,6 +1106,20 @@
         }
         printed |= dumper.didPrint();
 
+        dumper.setHeading("Deferred LOCKED_BOOT_COMPLETED broadcasts");
+        dumper.setLabel("Deferred LOCKED_BOOT_COMPLETED Broadcast");
+        for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
+            mUser2Deferred.valueAt(i).dump(dumper, Intent.ACTION_LOCKED_BOOT_COMPLETED);
+        }
+        printed |= dumper.didPrint();
+
+        dumper.setHeading("Deferred BOOT_COMPLETED broadcasts");
+        dumper.setLabel("Deferred BOOT_COMPLETED Broadcast");
+        for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
+            mUser2Deferred.valueAt(i).dump(dumper, Intent.ACTION_BOOT_COMPLETED);
+        }
+        printed |= dumper.didPrint();
+
         return printed;
     }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a83fdd0..ea63c08 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -249,11 +249,13 @@
     }
 
     public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
+        r.enqueueClockTime = System.currentTimeMillis();
         mParallelBroadcasts.add(r);
         enqueueBroadcastHelper(r);
     }
 
     public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
+        r.enqueueClockTime = System.currentTimeMillis();
         mDispatcher.enqueueOrderedBroadcastLocked(r);
         enqueueBroadcastHelper(r);
     }
@@ -263,8 +265,6 @@
      * enqueueOrderedBroadcastLocked.
      */
     private void enqueueBroadcastHelper(BroadcastRecord r) {
-        r.enqueueClockTime = System.currentTimeMillis();
-
         if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
             Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                 createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
@@ -368,6 +368,15 @@
         }
     }
 
+    /**
+     * Called by ActivityManagerService to notify that the uid has process started, if there is any
+     * deferred BOOT_COMPLETED broadcast, the BroadcastDispatcher can dispatch the broadcast now.
+     * @param uid
+     */
+    public void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
+        mDispatcher.updateUidReadyForBootCompletedBroadcastLocked(uid);
+    }
+
     public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
         boolean didSomething = false;
         final BroadcastRecord br = mPendingBroadcast;
@@ -1858,9 +1867,7 @@
     }
 
     private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
-        // TODO (206518114): Only allow apps with ACCESS_PACKAGE_USAGE_STATS to set
-        // getIdForResponseEvent.
-        // TODO (217251579): Temporarily use temp-allowlist reason to identify
+        // STOPSHIP (217251579): Temporarily use temp-allowlist reason to identify
         // push messages and record response events.
         useTemporaryAllowlistReasonAsSignal(r);
         if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 8015596..8b1e829 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -16,9 +16,20 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
+
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_ALL;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_CHANGE_ID;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
+import android.app.compat.CompatChanges;
 import android.content.ComponentName;
 import android.content.IIntentReceiver;
 import android.content.Intent;
@@ -30,6 +41,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.PrintWriterPrinter;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
@@ -371,6 +383,80 @@
         return split;
     }
 
+    /**
+     * Split a BroadcastRecord to a map of deferred receiver UID to deferred BroadcastRecord.
+     *
+     * The receivers that are deferred are removed from original BroadcastRecord's receivers list.
+     * The receivers that are not deferred are kept in original BroadcastRecord's receivers list.
+     *
+     * Only used to split LOCKED_BOOT_COMPLETED or BOOT_COMPLETED BroadcastRecord.
+     * LOCKED_BOOT_COMPLETED or BOOT_COMPLETED broadcast can be deferred until the first time
+     * the receiver's UID has a process started.
+     *
+     * @param ams The ActivityManagerService object.
+     * @param deferType Defer what UID?
+     * @return the deferred UID to BroadcastRecord map, the BroadcastRecord has the list of
+     *         receivers in that UID.
+     */
+    @NonNull SparseArray<BroadcastRecord> splitDeferredBootCompletedBroadcastLocked(
+            ActivityManagerInternal activityManagerInternal,
+            @BroadcastConstants.DeferBootCompletedBroadcastType int deferType) {
+        final SparseArray<BroadcastRecord> ret = new SparseArray<>();
+        if (deferType == DEFER_BOOT_COMPLETED_BROADCAST_NONE) {
+            return ret;
+        }
+
+        final String action = intent.getAction();
+        if (!Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)
+                && !Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+            return ret;
+        }
+
+        final SparseArray<List<Object>> uid2receiverList = new SparseArray<>();
+        for (int i = receivers.size() - 1; i >= 0; i--) {
+            final Object receiver = receivers.get(i);
+            final int uid = getReceiverUid(receiver);
+            if (deferType != DEFER_BOOT_COMPLETED_BROADCAST_ALL) {
+                if ((deferType & DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY) != 0) {
+                    if (activityManagerInternal.getRestrictionLevel(uid)
+                            < RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
+                        // skip if the UID is not background restricted.
+                        continue;
+                    }
+                }
+                if ((deferType & DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY) != 0) {
+                    if (!CompatChanges.isChangeEnabled(DEFER_BOOT_COMPLETED_BROADCAST_CHANGE_ID,
+                            uid)) {
+                        // skip if the UID is not targetSdkVersion T+.
+                        continue;
+                    }
+                }
+            }
+            // Remove receiver from original BroadcastRecord's receivers list.
+            receivers.remove(i);
+            final List<Object> receiverList = uid2receiverList.get(uid);
+            if (receiverList != null) {
+                receiverList.add(0, receiver);
+            } else {
+                ArrayList<Object> splitReceivers = new ArrayList<>();
+                splitReceivers.add(0, receiver);
+                uid2receiverList.put(uid, splitReceivers);
+            }
+        }
+        final int uidSize = uid2receiverList.size();
+        for (int i = 0; i < uidSize; i++) {
+            final BroadcastRecord br = new BroadcastRecord(queue, intent, callerApp, callerPackage,
+                    callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
+                    requiredPermissions, excludedPermissions, appOp, options,
+                    uid2receiverList.valueAt(i), resultTo,
+                    resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId,
+                    allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt);
+            br.enqueueClockTime = this.enqueueClockTime;
+            ret.put(uid2receiverList.keyAt(i), br);
+        }
+        return ret;
+    }
+
     int getReceiverUid(Object receiver) {
         if (receiver instanceof BroadcastFilter) {
             return ((BroadcastFilter) receiver).owningUid;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 711c576..bf2876f 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -1098,6 +1098,10 @@
                                 userId);
 
                         foregroundNoti = localForegroundNoti; // save it for amending next time
+
+                        signalForegroundServiceNotification(packageName, appInfo.uid,
+                                localForegroundId);
+
                     } catch (RuntimeException e) {
                         Slog.w(TAG, "Error showing notification for service", e);
                         // If it gave us a garbage notification, it doesn't
@@ -1131,10 +1135,21 @@
                 } catch (RuntimeException e) {
                     Slog.w(TAG, "Error canceling notification for service", e);
                 }
+                signalForegroundServiceNotification(packageName, appInfo.uid, -localForegroundId);
             }
         });
     }
 
+    private void signalForegroundServiceNotification(String packageName, int uid,
+            int foregroundId) {
+        synchronized (ams) {
+            for (int i = ams.mForegroundServiceStateListeners.size() - 1; i >= 0; i--) {
+                ams.mForegroundServiceStateListeners.get(i).onForegroundServiceNotificationUpdated(
+                        packageName, appInfo.uid, foregroundId);
+            }
+        }
+    }
+
     public void stripForegroundServiceFlagFromNotification() {
         final int localForegroundId = foregroundId;
         final int localUserId = userId;
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
index ab4d856..b73cf5b 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
@@ -29,6 +29,7 @@
 import android.app.ambientcontext.AmbientContextEvent;
 import android.app.ambientcontext.AmbientContextEventRequest;
 import android.app.ambientcontext.AmbientContextManager;
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -39,7 +40,6 @@
 import android.os.Bundle;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
-import android.provider.Settings;
 import android.service.ambientcontext.AmbientContextDetectionResult;
 import android.service.ambientcontext.AmbientContextDetectionServiceStatus;
 import android.text.TextUtils;
@@ -289,7 +289,7 @@
             return;
         }
 
-        if ((recentTasks != null) || recentTasks.getList().isEmpty()) {
+        if ((recentTasks == null) || recentTasks.getList().isEmpty()) {
             Slog.e(TAG, "Recent task list is empty!");
             return;
         }
@@ -297,40 +297,48 @@
         task = recentTasks.getList().get(0);
         if (!callingPackage.equals(task.topActivityInfo.packageName)) {
             Slog.e(TAG, "Recent task package name: " + task.topActivityInfo.packageName
-                    + " doesn't match with camera client package name: " + callingPackage);
+                    + " doesn't match with client package name: " + callingPackage);
             return;
         }
 
         // Start activity as the same task from the callingPackage
-        Intent intent = new Intent();
         ComponentName consentComponent = getConsentComponent();
-        if (consentComponent != null) {
-            Slog.e(TAG, "Starting consent activity for " + callingPackage);
-            Context context = getContext();
-            String packageNameExtraKey = Settings.Secure.getStringForUser(
-                    context.getContentResolver(),
-                    Settings.Secure.AMBIENT_CONTEXT_PACKAGE_NAME_EXTRA_KEY,
-                    userId);
-            String eventArrayExtraKey = Settings.Secure.getStringForUser(
-                    context.getContentResolver(),
-                    Settings.Secure.AMBIENT_CONTEXT_EVENT_ARRAY_EXTRA_KEY,
-                    userId);
+        if (consentComponent == null) {
+            Slog.e(TAG, "Consent component not found!");
+            return;
+        }
 
-            // Create consent activity intent with the calling package name and requested events.
+        Slog.d(TAG, "Starting consent activity for " + callingPackage);
+        Intent intent = new Intent();
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Context context = getContext();
+            String packageNameExtraKey = context.getResources().getString(
+                    com.android.internal.R.string.config_ambientContextPackageNameExtraKey);
+            String eventArrayExtraKey = context.getResources().getString(
+                    com.android.internal.R.string.config_ambientContextEventArrayExtraKey);
+
+            // Create consent activity intent with the calling package name and requested events
             intent.setComponent(consentComponent);
             if (packageNameExtraKey != null) {
                 intent.putExtra(packageNameExtraKey, callingPackage);
+            } else {
+                Slog.d(TAG, "Missing packageNameExtraKey for consent activity");
             }
             if (eventArrayExtraKey != null) {
                 intent.putExtra(eventArrayExtraKey, eventTypes);
+            } else {
+                Slog.d(TAG, "Missing eventArrayExtraKey for consent activity");
             }
 
             // Set parent to the calling app's task
             ActivityOptions options = ActivityOptions.makeBasic();
             options.setLaunchTaskId(task.taskId);
-            context.startActivity(intent, options.toBundle());
-        } else {
-            Slog.e(TAG, "Consent component not found!");
+            context.startActivityAsUser(intent, options.toBundle(), context.getUser());
+        } catch (ActivityNotFoundException e) {
+            Slog.e(TAG, "unable to start consent activity");
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -339,13 +347,12 @@
      */
     private ComponentName getConsentComponent() {
         Context context = getContext();
-        String consentComponent = Settings.Secure.getStringForUser(
-                context.getContentResolver(),
-                Settings.Secure.AMBIENT_CONTEXT_CONSENT_COMPONENT,
-                getUserId());
+        String consentComponent = context.getResources().getString(
+                    com.android.internal.R.string.config_defaultAmbientContextConsentComponent);
         if (TextUtils.isEmpty(consentComponent)) {
             return null;
         }
+        Slog.i(TAG, "Consent component name: " + consentComponent);
         return ComponentName.unflattenFromString(consentComponent);
     }
 
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 551773e..9ba9d78 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -17,8 +17,8 @@
 package com.android.server.app;
 
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
-import static android.content.Intent.ACTION_PACKAGE_CHANGED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.EXTRA_REPLACING;
 
 import static com.android.internal.R.styleable.GameModeConfig_allowGameAngleDriver;
 import static com.android.internal.R.styleable.GameModeConfig_allowGameDownscaling;
@@ -1467,7 +1467,6 @@
     private void registerPackageReceiver() {
         final IntentFilter packageFilter = new IntentFilter();
         packageFilter.addAction(ACTION_PACKAGE_ADDED);
-        packageFilter.addAction(ACTION_PACKAGE_CHANGED);
         packageFilter.addAction(ACTION_PACKAGE_REMOVED);
         packageFilter.addDataScheme("package");
         final BroadcastReceiver packageReceiver = new BroadcastReceiver() {
@@ -1492,16 +1491,29 @@
                     }
                     switch (intent.getAction()) {
                         case ACTION_PACKAGE_ADDED:
-                        case ACTION_PACKAGE_CHANGED:
                             updateConfigsForUser(userId, packageName);
                             break;
                         case ACTION_PACKAGE_REMOVED:
                             disableCompatScale(packageName);
-                            synchronized (mOverrideConfigLock) {
-                                mOverrideConfigs.remove(packageName);
-                            }
-                            synchronized (mDeviceConfigLock) {
-                                mConfigs.remove(packageName);
+                            // If EXTRA_REPLACING is true, it means there will be an
+                            // ACTION_PACKAGE_ADDED triggered after this because this
+                            // is an updated package that gets installed. Hence, disable
+                            // resolution downscaling effort but avoid removing the server
+                            // or commandline overriding configurations because those will
+                            // not change but the package game mode configurations may change
+                            // which may opt in and/or opt out some game mode configurations.
+                            if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
+                                synchronized (mOverrideConfigLock) {
+                                    mOverrideConfigs.remove(packageName);
+                                }
+                                synchronized (mDeviceConfigLock) {
+                                    mConfigs.remove(packageName);
+                                }
+                                synchronized (mLock) {
+                                    if (mSettings.containsKey(userId)) {
+                                        mSettings.get(userId).removeGame(packageName);
+                                    }
+                                }
                             }
                             break;
                         default:
diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java
index 2982545..1455a61 100644
--- a/services/core/java/com/android/server/app/GameManagerSettings.java
+++ b/services/core/java/com/android/server/app/GameManagerSettings.java
@@ -93,6 +93,14 @@
     }
 
     /**
+     * Remove the game mode of a given package.
+     * This operation must be synced with an external lock.
+     */
+    void removeGame(String packageName) {
+        mGameModes.remove(packageName);
+    }
+
+    /**
      * Write all current game service settings into disk.
      * This operation must be synced with an external lock.
      */
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d2c6c13..3e5e435 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -8574,6 +8574,30 @@
         return mSpatializerHelper.isAvailable();
     }
 
+    /** @see Spatializer#isAvailableForDevice(AudioDeviceAttributes) */
+    public boolean isSpatializerAvailableForDevice(@NonNull AudioDeviceAttributes device)  {
+        enforceModifyDefaultAudioEffectsPermission();
+        return mSpatializerHelper.isAvailableForDevice(Objects.requireNonNull(device));
+    }
+
+    /** @see Spatializer#hasHeadTracker(AudioDeviceAttributes) */
+    public boolean hasHeadTracker(@NonNull AudioDeviceAttributes device) {
+        enforceModifyDefaultAudioEffectsPermission();
+        return mSpatializerHelper.hasHeadTracker(Objects.requireNonNull(device));
+    }
+
+    /** @see Spatializer#setHeadTrackerEnabled(boolean, AudioDeviceAttributes) */
+    public void setHeadTrackerEnabled(boolean enabled, @NonNull AudioDeviceAttributes device) {
+        enforceModifyDefaultAudioEffectsPermission();
+        mSpatializerHelper.setHeadTrackerEnabled(enabled, Objects.requireNonNull(device));
+    }
+
+    /** @see Spatializer#isHeadTrackerEnabled(AudioDeviceAttributes) */
+    public boolean isHeadTrackerEnabled(@NonNull AudioDeviceAttributes device) {
+        enforceModifyDefaultAudioEffectsPermission();
+        return mSpatializerHelper.isHeadTrackerEnabled(Objects.requireNonNull(device));
+    }
+
     /** @see Spatializer#setSpatializerEnabled(boolean) */
     public void setSpatializerEnabled(boolean enabled) {
         enforceModifyDefaultAudioEffectsPermission();
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 106cbba..63b27d8 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -23,6 +23,7 @@
 import android.hardware.SensorManager;
 import android.media.AudioAttributes;
 import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
 import android.media.AudioFormat;
 import android.media.AudioSystem;
 import android.media.INativeSpatializerCallback;
@@ -33,11 +34,14 @@
 import android.media.ISpatializerHeadTrackingModeCallback;
 import android.media.ISpatializerOutputCallback;
 import android.media.SpatializationLevel;
+import android.media.SpatializationMode;
 import android.media.Spatializer;
 import android.media.SpatializerHeadTrackingMode;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.Pair;
+import android.util.SparseIntArray;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -69,10 +73,42 @@
     // TODO: replace with generic head tracker sensor name.
     //       the current implementation refers to the "google" namespace but will be replaced
     //       by an android name at the next API level revision, it is not Google-specific.
-    //       Also see "TODO-HT" in onInitSensors() method
     private static final String HEADTRACKER_SENSOR =
             "com.google.hardware.sensor.hid_dynamic.headtracker";
 
+    private static final SparseIntArray SPAT_MODE_FOR_DEVICE_TYPE = new SparseIntArray(15) {
+        {
+            append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL);
+            append(AudioDeviceInfo.TYPE_WIRED_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
+            append(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, SpatializationMode.SPATIALIZER_BINAURAL);
+            // assumption for A2DP: mostly headsets
+            append(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, SpatializationMode.SPATIALIZER_BINAURAL);
+            append(AudioDeviceInfo.TYPE_DOCK, SpatializationMode.SPATIALIZER_TRANSAURAL);
+            append(AudioDeviceInfo.TYPE_USB_ACCESSORY, SpatializationMode.SPATIALIZER_TRANSAURAL);
+            append(AudioDeviceInfo.TYPE_USB_DEVICE, SpatializationMode.SPATIALIZER_TRANSAURAL);
+            append(AudioDeviceInfo.TYPE_USB_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
+            append(AudioDeviceInfo.TYPE_LINE_ANALOG, SpatializationMode.SPATIALIZER_TRANSAURAL);
+            append(AudioDeviceInfo.TYPE_LINE_DIGITAL, SpatializationMode.SPATIALIZER_TRANSAURAL);
+            append(AudioDeviceInfo.TYPE_AUX_LINE, SpatializationMode.SPATIALIZER_TRANSAURAL);
+            append(AudioDeviceInfo.TYPE_HEARING_AID, SpatializationMode.SPATIALIZER_BINAURAL);
+            append(AudioDeviceInfo.TYPE_BLE_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
+            append(AudioDeviceInfo.TYPE_BLE_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL);
+            // assumption that BLE broadcast would be mostly consumed on headsets
+            append(AudioDeviceInfo.TYPE_BLE_BROADCAST, SpatializationMode.SPATIALIZER_BINAURAL);
+        }
+    };
+
+    private static final int[] WIRELESS_TYPES = { AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
+            AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+            AudioDeviceInfo.TYPE_BLE_HEADSET,
+            AudioDeviceInfo.TYPE_BLE_SPEAKER,
+            AudioDeviceInfo.TYPE_BLE_BROADCAST
+    };
+
+    private static final int[] WIRELESS_SPEAKER_TYPES = {
+            AudioDeviceInfo.TYPE_BLE_SPEAKER,
+    };
+
     // Spatializer state machine
     private static final int STATE_UNINITIALIZED = 0;
     private static final int STATE_NOT_SUPPORTED = 1;
@@ -82,11 +118,20 @@
     private static final int STATE_DISABLED_AVAILABLE = 6;
     private int mState = STATE_UNINITIALIZED;
 
+    private boolean mFeatureEnabled = false;
     /** current level as reported by native Spatializer in callback */
     private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
     private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+    private boolean mTransauralSupported = false;
+    private boolean mBinauralSupported = false;
     private int mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
     private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
+    /**
+     *  The desired head tracking mode when enabling head tracking, tracks mDesiredHeadTrackingMode,
+     *  except when head tracking gets disabled through setting the desired mode to
+     *  {@link Spatializer#HEAD_TRACKING_MODE_DISABLED}.
+     */
+    private int mDesiredHeadTrackingModeWhenEnabled = Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
     private int mSpatOutput = 0;
     private @Nullable ISpatializer mSpat;
     private @Nullable SpatializerCallback mSpatCallback;
@@ -108,8 +153,18 @@
 
     //---------------------------------------------------------------
     // audio device compatibility / enabled
+    /**
+     * List of device types that can be used on this device with Spatial Audio.
+     * It is initialized based on the transaural/binaural capabilities
+     * of the effect.
+     */
+    private final ArrayList<Integer> mSACapableDeviceTypes = new ArrayList<>(0);
 
-    private final ArrayList<AudioDeviceAttributes> mCompatibleAudioDevices = new ArrayList<>(0);
+    /**
+     * List of devices where Spatial Audio is possible. Each device can be enabled or disabled
+     * (== user choice to use or not)
+     */
+    private final ArrayList<SADeviceState> mSADevices = new ArrayList<>(0);
 
     //------------------------------------------------------
     // initialization
@@ -155,6 +210,46 @@
                     break;
                 }
             }
+            byte[] spatModes = spat.getSupportedModes();
+            for (byte mode : spatModes) {
+                switch (mode) {
+                    case SpatializationMode.SPATIALIZER_BINAURAL:
+                        mBinauralSupported = true;
+                        break;
+                    case SpatializationMode.SPATIALIZER_TRANSAURAL:
+                        mTransauralSupported = true;
+                        break;
+                    default:
+                        Log.e(TAG, "Spatializer reports unknown supported mode:" + mode);
+                        break;
+                }
+            }
+            // if neither transaural nor binaural is supported, bail
+            if (!mBinauralSupported && !mTransauralSupported) {
+                mState = STATE_NOT_SUPPORTED;
+                return;
+            }
+
+            // initialize list of compatible devices
+            for (int i = 0; i < SPAT_MODE_FOR_DEVICE_TYPE.size(); i++) {
+                int mode = SPAT_MODE_FOR_DEVICE_TYPE.valueAt(i);
+                if ((mode == (int) SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported)
+                        || (mode == (int) SpatializationMode.SPATIALIZER_TRANSAURAL
+                            && mTransauralSupported)) {
+                    mSACapableDeviceTypes.add(SPAT_MODE_FOR_DEVICE_TYPE.keyAt(i));
+                }
+            }
+            if (mTransauralSupported) {
+                // TODO deal with persisted values
+                mSADevices.add(
+                        new SADeviceState(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null));
+            }
+            if (mBinauralSupported) {
+                // TODO deal with persisted values
+                mSADevices.add(
+                        new SADeviceState(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, null));
+            }
+            // TODO read persisted states
         } catch (RemoteException e) {
             /* capable level remains at NONE*/
         } finally {
@@ -184,12 +279,15 @@
         mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
         mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
         init(true);
-        setFeatureEnabled(featureEnabled);
+        setSpatializerEnabledInt(featureEnabled);
     }
 
     //------------------------------------------------------
     // routing monitoring
-    void onRoutingUpdated() {
+    synchronized void onRoutingUpdated() {
+        if (!mFeatureEnabled) {
+            return;
+        }
         switch (mState) {
             case STATE_UNINITIALIZED:
             case STATE_NOT_SUPPORTED:
@@ -201,11 +299,36 @@
                 break;
         }
         mASA.getDevicesForAttributes(DEFAULT_ATTRIBUTES).toArray(ROUTING_DEVICES);
-        final boolean able =
-                AudioSystem.canBeSpatialized(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, ROUTING_DEVICES);
-        logd("onRoutingUpdated: can spatialize media 5.1:" + able
-                + " on device:" + ROUTING_DEVICES[0]);
-        setDispatchAvailableState(able);
+
+        // is media routed to a new device?
+        if (isWireless(ROUTING_DEVICES[0].getType())) {
+            addWirelessDeviceIfNew(ROUTING_DEVICES[0]);
+        }
+
+        // find if media device enabled / available
+        final Pair<Boolean, Boolean> enabledAvailable = evaluateState(ROUTING_DEVICES[0]);
+
+        boolean able = false;
+        if (enabledAvailable.second) {
+            // available for Spatial audio, check w/ effect
+            able = canBeSpatializedOnDevice(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, ROUTING_DEVICES);
+            Log.i(TAG, "onRoutingUpdated: can spatialize media 5.1:" + able
+                    + " on device:" + ROUTING_DEVICES[0]);
+            setDispatchAvailableState(able);
+        } else {
+            Log.i(TAG, "onRoutingUpdated: device:" + ROUTING_DEVICES[0]
+                    + " not available for Spatial Audio");
+            setDispatchAvailableState(false);
+        }
+
+        if (able && enabledAvailable.first) {
+            Log.i(TAG, "Enabling Spatial Audio since enabled for media device:"
+                    + ROUTING_DEVICES[0]);
+        } else {
+            Log.i(TAG, "Disabling Spatial Audio since disabled for media device:"
+                    + ROUTING_DEVICES[0]);
+        }
+        setDispatchFeatureEnabledState(able && enabledAvailable.first);
 
         if (mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED
                 && mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_DISABLED) {
@@ -298,20 +421,145 @@
     //------------------------------------------------------
     // compatible devices
     /**
-     * @return a shallow copy of the list of compatible audio devices
+     * Return the list of compatible devices, which reflects the device compatible with the
+     * spatializer effect, and those that have been explicitly enabled or disabled
+     * @return the list of compatible audio devices
      */
     synchronized @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() {
-        return (List<AudioDeviceAttributes>) mCompatibleAudioDevices.clone();
+        // build unionOf(mCompatibleAudioDevices, mEnabledDevice) - mDisabledAudioDevices
+        ArrayList<AudioDeviceAttributes> compatList = new ArrayList<>();
+        for (SADeviceState dev : mSADevices) {
+            if (dev.mEnabled) {
+                compatList.add(new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
+                        dev.mDeviceType, dev.mDeviceAddress == null ? "" : dev.mDeviceAddress));
+            }
+        }
+        return compatList;
     }
 
     synchronized void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
-        if (!mCompatibleAudioDevices.contains(ada)) {
-            mCompatibleAudioDevices.add(ada);
+        // TODO add log
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+        boolean updateRouting = false;
+        boolean isInList = false;
+
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                isInList = true;
+                // state change?
+                updateRouting = !deviceState.mEnabled;
+                deviceState.mEnabled = true;
+                break;
+            }
+        }
+        if (!isInList) {
+            final SADeviceState dev = new SADeviceState(deviceType,
+                    wireless ? ada.getAddress() : null);
+            dev.mEnabled = true;
+            mSADevices.add(dev);
+            updateRouting = true;
+        }
+        if (updateRouting) {
+            onRoutingUpdated();
         }
     }
 
     synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
-        mCompatibleAudioDevices.remove(ada);
+        // TODO add log
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+        boolean updateRouting = false;
+
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                // state change?
+                updateRouting = deviceState.mEnabled;
+                deviceState.mEnabled = false;
+                break;
+            }
+        }
+        if (updateRouting) {
+            onRoutingUpdated();
+        }
+    }
+
+    /**
+     * Return if Spatial Audio is enabled and available for the given device
+     * @param ada
+     * @return a pair of boolean, 1/ enabled? 2/ available?
+     */
+    private synchronized Pair<Boolean, Boolean> evaluateState(AudioDeviceAttributes ada) {
+        // if not a wireless device, this value will be overwritten to map the type
+        // to TYPE_BUILTIN_SPEAKER or TYPE_WIRED_HEADPHONES
+        int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+
+        // if not a wireless device: find if media device is in the speaker, wired headphones
+        if (!wireless) {
+            // is the device type capable of doing SA?
+            if (!mSACapableDeviceTypes.contains(deviceType)) {
+                Log.i(TAG, "Device incompatible with Spatial Audio dev:" + ada);
+                return new Pair<>(false, false);
+            }
+            // what spatialization mode to use for this device?
+            final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
+            if (spatMode == Integer.MIN_VALUE) {
+                // error case, device not found
+                Log.e(TAG, "no spatialization mode found for device type:" + deviceType);
+                return new Pair<>(false, false);
+            }
+            // map the spatialization mode to the SPEAKER or HEADPHONES device
+            if (spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL) {
+                deviceType = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+            } else {
+                deviceType = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
+            }
+        } else { // wireless device
+            if (isWirelessSpeaker(deviceType) && !mTransauralSupported) {
+                Log.i(TAG, "Device incompatible with Spatial Audio (no transaural) dev:"
+                        + ada);
+                return new Pair<>(false, false);
+            }
+            if (!mBinauralSupported) {
+                Log.i(TAG, "Device incompatible with Spatial Audio (no binaural) dev:"
+                        + ada);
+                return new Pair<>(false, false);
+            }
+        }
+
+        boolean enabled = false;
+        boolean available = false;
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                available = true;
+                enabled = deviceState.mEnabled;
+                break;
+            }
+        }
+        return new Pair<>(enabled, available);
+    }
+
+    private synchronized void addWirelessDeviceIfNew(@NonNull AudioDeviceAttributes ada) {
+        boolean knownDevice = false;
+        for (SADeviceState deviceState : mSADevices) {
+            // wireless device so always check address
+            if (ada.getType() == deviceState.mDeviceType
+                    && ada.getAddress().equals(deviceState.mDeviceAddress)) {
+                knownDevice = true;
+                break;
+            }
+        }
+        if (!knownDevice) {
+            mSADevices.add(new SADeviceState(ada.getType(), ada.getAddress()));
+            //### TODO persist list
+        }
     }
 
     //------------------------------------------------------
@@ -345,7 +593,56 @@
         }
     }
 
+    synchronized boolean isAvailableForDevice(@NonNull AudioDeviceAttributes ada) {
+        if (ada.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
+            return false;
+        }
+
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes,
+            @NonNull AudioFormat format, @NonNull AudioDeviceAttributes[] devices) {
+        final byte modeForDevice = (byte) SPAT_MODE_FOR_DEVICE_TYPE.get(devices[0].getType(),
+                /*default when type not found*/ SpatializationMode.SPATIALIZER_BINAURAL);
+        if ((modeForDevice == SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported)
+                || (modeForDevice == SpatializationMode.SPATIALIZER_TRANSAURAL
+                        && mTransauralSupported)) {
+            return AudioSystem.canBeSpatialized(attributes, format, devices);
+        }
+        return false;
+    }
+
     synchronized void setFeatureEnabled(boolean enabled) {
+        if (mFeatureEnabled == enabled) {
+            return;
+        }
+        mFeatureEnabled = enabled;
+        if (mFeatureEnabled) {
+            if (mState == STATE_NOT_SUPPORTED) {
+                Log.e(TAG, "Can't enabled Spatial Audio, unsupported");
+                return;
+            }
+            if (mState == STATE_UNINITIALIZED) {
+                init(true);
+            }
+            setSpatializerEnabledInt(true);
+            onRoutingUpdated();
+        } else {
+            setSpatializerEnabledInt(false);
+        }
+    }
+
+    synchronized void setSpatializerEnabledInt(boolean enabled) {
         switch (mState) {
             case STATE_UNINITIALIZED:
                 if (enabled) {
@@ -397,8 +694,7 @@
     }
 
     /**
-     * precondition: mState = STATE_*
-     *               isFeatureEnabled() != featureEnabled
+     * Update the feature state, no-op if no change
      * @param featureEnabled
      */
     private synchronized void setDispatchFeatureEnabledState(boolean featureEnabled) {
@@ -410,6 +706,10 @@
                 case STATE_DISABLED_AVAILABLE:
                     mState = STATE_ENABLED_AVAILABLE;
                     break;
+                case STATE_ENABLED_AVAILABLE:
+                case STATE_ENABLED_UNAVAILABLE:
+                    // already enabled: no-op
+                    return;
                 default:
                     throw(new IllegalStateException("Invalid mState:" + mState
                             + " for enabled true"));
@@ -422,6 +722,10 @@
                 case STATE_ENABLED_AVAILABLE:
                     mState = STATE_DISABLED_AVAILABLE;
                     break;
+                case STATE_DISABLED_AVAILABLE:
+                case STATE_DISABLED_UNAVAILABLE:
+                    // already disabled: no-op
+                    return;
                 default:
                     throw (new IllegalStateException("Invalid mState:" + mState
                             + " for enabled false"));
@@ -562,7 +866,7 @@
         AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1];
         // going through adapter to take advantage of routing cache
         mASA.getDevicesForAttributes(attributes).toArray(devices);
-        final boolean able = AudioSystem.canBeSpatialized(attributes, format, devices);
+        final boolean able = canBeSpatializedOnDevice(attributes, format, devices);
         logd("canBeSpatialized returning " + able);
         return able;
     }
@@ -688,6 +992,9 @@
         if (!checkSpatForHeadTracking("setDesiredHeadTrackingMode")) {
             return;
         }
+        if (mode != Spatializer.HEAD_TRACKING_MODE_DISABLED) {
+            mDesiredHeadTrackingModeWhenEnabled = mode;
+        }
         try {
             if (mDesiredHeadTrackingMode != mode) {
                 mDesiredHeadTrackingMode = mode;
@@ -701,6 +1008,84 @@
         }
     }
 
+    synchronized void setHeadTrackerEnabled(boolean enabled, @NonNull AudioDeviceAttributes ada) {
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                if (!deviceState.mHasHeadTracker) {
+                    Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled
+                            + " device:" + ada + " on a device without headtracker");
+                    return;
+                }
+                Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada);
+                deviceState.mHeadTrackerEnabled = enabled;
+                break;
+            }
+        }
+        // check current routing to see if it affects the headtracking mode
+        if (ROUTING_DEVICES[0].getType() == deviceType
+                && ROUTING_DEVICES[0].getAddress().equals(ada.getAddress())) {
+            setDesiredHeadTrackingMode(enabled ? mDesiredHeadTrackingModeWhenEnabled
+                    : Spatializer.HEAD_TRACKING_MODE_DISABLED);
+        }
+    }
+
+    synchronized boolean hasHeadTracker(@NonNull AudioDeviceAttributes ada) {
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                return deviceState.mHasHeadTracker;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Configures device in list as having a head tracker
+     * @param ada
+     * @return true if the head tracker is enabled, false otherwise or if device not found
+     */
+    synchronized boolean setHasHeadTracker(@NonNull AudioDeviceAttributes ada) {
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                deviceState.mHasHeadTracker = true;
+                return deviceState.mHeadTrackerEnabled;
+            }
+        }
+        Log.e(TAG, "setHasHeadTracker: device not found for:" + ada);
+        return false;
+    }
+
+    synchronized boolean isHeadTrackerEnabled(@NonNull AudioDeviceAttributes ada) {
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                if (!deviceState.mHasHeadTracker) {
+                    return false;
+                }
+                return deviceState.mHeadTrackerEnabled;
+            }
+        }
+        return false;
+    }
+
     private boolean checkSpatForHeadTracking(String funcName) {
         switch (mState) {
             case STATE_UNINITIALIZED:
@@ -884,7 +1269,7 @@
     }
 
     synchronized void onInitSensors() {
-        final boolean init = (mSpatLevel != SpatializationLevel.NONE);
+        final boolean init = mFeatureEnabled && (mSpatLevel != SpatializationLevel.NONE);
         final String action = init ? "initializing" : "releasing";
         if (mSpat == null) {
             Log.e(TAG, "not " + action + " sensors, null spatializer");
@@ -926,6 +1311,12 @@
                     UUID uuid = sensor.getUuid();
                     if (uuid.equals(routingDeviceUuid)) {
                         headHandle = sensor.getHandle();
+                        // TODO check risk of race condition:
+                        //     does this happen before routing is updated?
+                        //     avoid by supporting adding device here AND in onRoutingUpdated()
+                        if (!setHasHeadTracker(ROUTING_DEVICES[0])) {
+                            headHandle = -1;
+                        }
                         break;
                     }
                     if (uuid.equals(UuidUtils.STANDALONE_UUID)) {
@@ -933,6 +1324,7 @@
                     }
                 }
             }
+
             Sensor screenSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
             screenHandle = screenSensor.getHandle();
         } else {
@@ -1017,7 +1409,50 @@
         for (int mode : modes) {
             modesString += Spatializer.headtrackingModeToString(mode) + " ";
         }
+        pw.println("\tsupports binaural:" + mBinauralSupported + " / transaural"
+                + mTransauralSupported);
         pw.println("\tsupported head tracking modes:" + modesString);
         pw.println("\tmSpatOutput:" + mSpatOutput);
+        pw.println("\tdevices:\n");
+        for (SADeviceState device : mSADevices) {
+            pw.println("\t\t" + device + "\n");
+        }
+    }
+
+    private static final class SADeviceState {
+        final int mDeviceType;
+        final @Nullable String mDeviceAddress; // non-null for wireless devices
+        boolean mEnabled = true;               // by default, SA is enabled on any device
+        boolean mHasHeadTracker = false;
+        boolean mHeadTrackerEnabled = true;    // by default, if head tracker is present, use it
+
+        SADeviceState(int deviceType, @Nullable String address) {
+            mDeviceType = deviceType;
+            mDeviceAddress = address;
+        }
+
+        @Override
+        public String toString() {
+            return "type:" + mDeviceType + " addr:" + mDeviceAddress + " enabled:" + mEnabled
+                    + " HT:" + mHasHeadTracker + " HTenabled:" + mHeadTrackerEnabled;
+        }
+    }
+
+    private static boolean isWireless(int deviceType) {
+        for (int type : WIRELESS_TYPES) {
+            if (type == deviceType) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isWirelessSpeaker(int deviceType) {
+        for (int type: WIRELESS_SPEAKER_TYPES) {
+            if (type == deviceType) {
+                return true;
+            }
+        }
+        return false;
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 05c3f68..aec98f0 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -104,7 +104,8 @@
         final int requestedStrength = Utils.getPublicBiometricStrength(promptInfo);
         final boolean credentialRequested = Utils.isCredentialRequested(promptInfo);
 
-        final boolean credentialAvailable = trustManager.isDeviceSecure(userId);
+        final boolean credentialAvailable = trustManager.isDeviceSecure(userId,
+                context.getAssociatedDisplayId());
 
         // Assuming that biometric authenticators are listed in priority-order, the rest of this
         // function will attempt to find the first authenticator that's as strong or stronger than
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 135276e..94d3d15 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -67,4 +67,9 @@
      * display.
      */
     public abstract boolean isAppRunningOnAnyVirtualDevice(int uid);
+
+    /**
+     * Returns true if the {@code displayId} is owned by any virtual device
+     */
+    public abstract boolean isDisplayOwnedByAnyVirtualDevice(int displayId);
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 4d3e438..0f4648a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1717,7 +1717,7 @@
                     return false;
                 }
                 setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
-                notifyPasswordChanged(userId);
+                notifyPasswordChanged(credential, userId);
             }
             if (isCredentialSharedWithParent(userId)) {
                 // Make sure the profile doesn't get locked straight after setting work challenge.
@@ -2510,9 +2510,11 @@
      * Call after {@link #setUserPasswordMetrics} so metrics are updated before
      * reporting the password changed.
      */
-    private void notifyPasswordChanged(@UserIdInt int userId) {
+    private void notifyPasswordChanged(LockscreenCredential newCredential, @UserIdInt int userId) {
         mHandler.post(() -> {
-            mInjector.getDevicePolicyManager().reportPasswordChanged(userId);
+            mInjector.getDevicePolicyManager().reportPasswordChanged(
+                    PasswordMetrics.computeForCredential(newCredential),
+                    userId);
             LocalServices.getService(WindowManagerInternal.class).reportPasswordChanged(userId);
         });
     }
@@ -3447,7 +3449,7 @@
                 // the caller like DPMS), otherwise it can lead to deadlock.
                 mHandler.post(() -> unlockUser(userId, null));
             }
-            notifyPasswordChanged(userId);
+            notifyPasswordChanged(credential, userId);
             notifySeparateProfileChallengeChanged(userId);
         }
         return result;
diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
index fd141bd7..1ae7ac0 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -26,6 +26,7 @@
 import android.media.metrics.PlaybackStateEvent;
 import android.media.metrics.TrackChangeEvent;
 import android.os.Binder;
+import android.os.PersistableBundle;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.Properties;
 import android.util.Base64;
@@ -180,6 +181,20 @@
             StatsLog.write(statsEvent);
         }
 
+        public void reportBundleMetrics(String sessionId, PersistableBundle metrics, int userId) {
+            int level = loggingLevel();
+            if (level == LOGGING_LEVEL_BLOCKED) {
+                return;
+            }
+
+            int atomid = metrics.getInt("KEY_STATSD_ATOM_NUMBER");
+            switch (atomid) {
+                default:
+                    return;
+                // to be extended as we define statsd atoms
+            }
+        }
+
         @Override
         public void reportPlaybackStateEvent(
                 String sessionId, PlaybackStateEvent event, int userId) {
@@ -222,6 +237,21 @@
         }
 
         @Override
+        public String getTranscodingSessionId(int userId) {
+            return getSessionIdInternal(userId);
+        }
+
+        @Override
+        public String getEditingSessionId(int userId) {
+            return getSessionIdInternal(userId);
+        }
+
+        @Override
+        public String getBundleSessionId(int userId) {
+            return getSessionIdInternal(userId);
+        }
+
+        @Override
         public void reportPlaybackErrorEvent(
                 String sessionId, PlaybackErrorEvent event, int userId) {
             int level = loggingLevel();
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 86ac7c1..d80d9f3 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -178,6 +178,12 @@
             boolean userSet, boolean reviewRequired) {
         assertFlag();
         final long callingId = Binder.clearCallingIdentity();
+        // Do not change fixed permissions, and do not change non-user set permissions that are
+        // granted by default, or granted by role.
+        if (isPermissionFixed(packageName, userId)
+                || (isPermissionGrantedByDefaultOrRole(packageName, userId) && !userSet)) {
+            return;
+        }
         try {
             if (grant && !reviewRequired) {
                 mPermManager.grantRuntimePermission(packageName, NOTIFICATION_PERMISSION, userId);
@@ -252,6 +258,24 @@
         }
     }
 
+    boolean isPermissionGrantedByDefaultOrRole(String packageName, @UserIdInt int userId) {
+        assertFlag();
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            try {
+                int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
+                        userId);
+                return (flags & (PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT
+                        | PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE)) != 0;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Could not reach system server", e);
+            }
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
     private void assertFlag() {
         if (!mMigrationEnabled) {
             throw new IllegalStateException("Method called without checking flag value");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index e8ad2bb..1185890 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -391,6 +391,7 @@
 
                             if (migrateToPermission) {
                                 r.importance = appImportance;
+                                r.migrateToPm = true;
                                 if (r.uid != UNKNOWN_UID) {
                                     // Don't call into permission system until we have a valid uid
                                     PackagePermission pkgPerm = new PackagePermission(
@@ -2575,7 +2576,7 @@
                         synchronized (mPackagePreferences) {
                             mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
                         }
-                        if (mPermissionHelper.isMigrationEnabled()) {
+                        if (mPermissionHelper.isMigrationEnabled() && r.migrateToPm) {
                             try {
                                 PackagePermission p = new PackagePermission(
                                         r.pkg, UserHandle.getUserId(r.uid),
@@ -2841,6 +2842,8 @@
         boolean userDemotedMsgApp = false;
         boolean hasSentValidBubble = false;
 
+        boolean migrateToPm = false;
+
         Delegate delegate = null;
         ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
         Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 5b2e097..4f1a9e7 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -33,11 +33,6 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.SigningDetails;
 import android.content.pm.UserInfo;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
 import android.os.Binder;
 import android.os.Process;
 import android.os.Trace;
@@ -62,6 +57,11 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedProvider;
 import com.android.server.utils.Snappable;
 import com.android.server.utils.SnapshotCache;
 import com.android.server.utils.Snapshots;
@@ -158,6 +158,7 @@
     private final FeatureConfig mFeatureConfig;
     private final OverlayReferenceMapper mOverlayReferenceMapper;
     private final StateProvider mStateProvider;
+    private final PackageManagerInternal mPmInternal;
 
     private SigningDetails mSystemSigningDetails;
     private Set<String> mProtectedBroadcasts = new ArraySet<>();
@@ -250,13 +251,15 @@
             String[] forceQueryableList,
             boolean systemAppsQueryable,
             @Nullable OverlayReferenceMapper.Provider overlayProvider,
-            Executor backgroundExecutor) {
+            Executor backgroundExecutor,
+            PackageManagerInternal pmInternal) {
         mFeatureConfig = featureConfig;
         mForceQueryableByDevicePackageNames = forceQueryableList;
         mSystemAppsQueryable = systemAppsQueryable;
         mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
                 overlayProvider);
         mStateProvider = stateProvider;
+        mPmInternal = pmInternal;
         mBackgroundExecutor = backgroundExecutor;
         mSnapshot = makeCache();
     }
@@ -289,6 +292,7 @@
         }
 
         mBackgroundExecutor = null;
+        mPmInternal = null;
         mSnapshot = new SnapshotCache.Sealed<>();
     }
 
@@ -508,7 +512,7 @@
         };
         AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig,
                 forcedQueryablePackageNames, forceSystemAppsQueryable, null,
-                injector.getBackgroundExecutor());
+                injector.getBackgroundExecutor(), pms);
         featureConfig.setAppsFilter(appsFilter);
         return appsFilter;
     }
@@ -1236,9 +1240,9 @@
             // shared user members to re-establish visibility between them and other packages.
             // NOTE: this must come after all removals from data structures but before we update the
             //       cache
-            if (setting.getSharedUser() != null) {
-                final ArraySet<? extends PackageStateInternal> sharedUserPackages =
-                        setting.getSharedUser().getPackageStates();
+            if (setting.hasSharedUser()) {
+                final ArraySet<PackageStateInternal> sharedUserPackages =
+                        mPmInternal.getSharedUserPackages(setting.getSharedUserAppId());
                 for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
                     if (sharedUserPackages.valueAt(i) == setting) {
                         continue;
@@ -1250,9 +1254,9 @@
 
             synchronized (mCacheLock) {
                 removeAppIdFromVisibilityCache(setting.getAppId());
-                if (mShouldFilterCache != null && setting.getSharedUser() != null) {
-                    final ArraySet<? extends PackageStateInternal> sharedUserPackages =
-                            setting.getSharedUser().getPackageStates();
+                if (mShouldFilterCache != null && setting.hasSharedUser()) {
+                    final ArraySet<PackageStateInternal> sharedUserPackages =
+                            mPmInternal.getSharedUserPackages(setting.getSharedUserAppId());
                     for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
                         PackageStateInternal siblingSetting =
                                 sharedUserPackages.valueAt(i);
@@ -1367,13 +1371,14 @@
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof");
             }
             if (callingSetting instanceof PackageStateInternal) {
-                if (((PackageStateInternal) callingSetting).getSharedUser() == null) {
-                    callingPkgSetting = (PackageStateInternal) callingSetting;
-                    callingSharedPkgSettings = null;
-                } else {
+                final PackageStateInternal packageState = (PackageStateInternal) callingSetting;
+                if (packageState.hasSharedUser()) {
                     callingPkgSetting = null;
-                    callingSharedPkgSettings = ((PackageStateInternal) callingSetting)
-                            .getSharedUser().getPackageStates();
+                    callingSharedPkgSettings = mPmInternal.getSharedUserPackages(
+                            packageState.getSharedUserAppId());
+                } else {
+                    callingPkgSetting = packageState;
+                    callingSharedPkgSettings = null;
                 }
             } else {
                 callingPkgSetting = null;
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 0ae3418..0d9ccd2 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -640,4 +640,12 @@
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @Nullable
     Pair<PackageStateInternal, SharedUserApi> getPackageOrSharedUser(int appId);
+
+    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+    @Nullable
+    SharedUserApi getSharedUser(int sharedUserAppIde);
+
+    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+    @NonNull
+    ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId);
 }
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 78aa45a..04f0aab 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -264,7 +264,7 @@
         }
 
         @Nullable
-        public SharedUserSetting getSharedUser(String name) {
+        public SharedUserSetting getSharedUserFromId(String name) {
             try {
                 return mSettings.getSharedUserLPw(name, 0, 0, false /*create*/);
             } catch (PackageManagerException ignored) {
@@ -298,6 +298,31 @@
         public Collection<SharedUserSetting> getAllSharedUsers() {
             return mSettings.getAllSharedUsersLPw();
         }
+
+        @Nullable
+        public SharedUserApi getSharedUserFromPackageName(String packageName) {
+            return mSettings.getSharedUserSettingLPr(packageName);
+        }
+
+        @Nullable
+        public SharedUserApi getSharedUserFromAppId(int sharedUserAppId) {
+            return (SharedUserSetting) mSettings.getSettingLPr(sharedUserAppId);
+        }
+
+        @NonNull
+        public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
+            final ArraySet<PackageStateInternal> res = new ArraySet<>();
+            final SharedUserSetting sharedUserSetting =
+                    (SharedUserSetting) mSettings.getSettingLPr(sharedUserAppId);
+            if (sharedUserSetting != null) {
+                final ArraySet<? extends PackageStateInternal> sharedUserPackages =
+                        sharedUserSetting.getPackageStates();
+                for (PackageStateInternal ps : sharedUserPackages) {
+                    res.add(ps);
+                }
+            }
+            return res;
+        }
     }
 
     private static final Comparator<ProviderInfo> sProviderInitOrderSorter = (p1, p2) -> {
@@ -1570,7 +1595,8 @@
             PackageInfo pi = new PackageInfo();
             pi.packageName = ps.getPackageName();
             pi.setLongVersionCode(ps.getVersionCode());
-            pi.sharedUserId = (ps.getSharedUser() != null) ? ps.getSharedUser().getName() : null;
+            SharedUserApi sharedUser = mSettings.getSharedUserFromPackageName(pi.packageName);
+            pi.sharedUserId = (sharedUser != null) ? sharedUser.getName() : null;
             pi.firstInstallTime = state.getFirstInstallTime();
             pi.lastUpdateTime = ps.getLastUpdateTime();
 
@@ -4306,7 +4332,7 @@
             if (shouldFilterApplication(sus, callingUid, callingUserId)) {
                 return null;
             }
-            return sus.name + ":" + sus.userId;
+            return sus.name + ":" + sus.mAppId;
         } else if (obj instanceof PackageSetting) {
             final PackageSetting ps = (PackageSetting) obj;
             if (shouldFilterApplication(ps, callingUid, callingUserId)) {
@@ -4366,10 +4392,10 @@
         if (getInstantAppPackageName(callingUid) != null) {
             return Process.INVALID_UID;
         }
-        final SharedUserSetting suid = mSettings.getSharedUser(sharedUserName);
+        final SharedUserSetting suid = mSettings.getSharedUserFromId(sharedUserName);
         if (suid != null && !shouldFilterApplication(suid, callingUid,
                 UserHandle.getUserId(callingUid))) {
-            return suid.userId;
+            return suid.mAppId;
         }
         return Process.INVALID_UID;
     }
@@ -5430,7 +5456,7 @@
     public SparseArray<String> getAppsWithSharedUserIds() {
         final SparseArray<String> sharedUserIds = new SparseArray<>();
         for (SharedUserSetting setting : mSettings.getAllSharedUsers()) {
-            sharedUserIds.put(UserHandle.getAppId(setting.userId), setting.name);
+            sharedUserIds.put(UserHandle.getAppId(setting.mAppId), setting.name);
         }
         return sharedUserIds;
     }
@@ -5440,12 +5466,12 @@
     public String[] getSharedUserPackagesForPackage(@NonNull String packageName,
             @UserIdInt int userId) {
         final PackageStateInternal packageSetting = mSettings.getPackage(packageName);
-        if (packageSetting == null || packageSetting.getSharedUser() == null) {
+        if (packageSetting == null || mSettings.getSharedUserFromPackageName(packageName) == null) {
             return EmptyArray.STRING;
         }
 
         ArraySet<? extends PackageStateInternal> packages =
-                packageSetting.getSharedUser().getPackageStates();
+                mSettings.getSharedUserFromPackageName(packageName).getPackageStates();
         final int numPackages = packages.size();
         String[] res = new String[numPackages];
         int i = 0;
@@ -5627,4 +5653,16 @@
     private int getSupplementalProcessUid() {
         return getPackage(mService.getSupplementalProcessPackageName()).getUid();
     }
+
+    @Nullable
+    @Override
+    public SharedUserApi getSharedUser(int sharedUserAppId) {
+        return mSettings.getSharedUserFromAppId(sharedUserAppId);
+    }
+
+    @NonNull
+    @Override
+    public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
+        return mSettings.getSharedUserPackages(sharedUserAppId);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index 40d4c03..583348b 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -852,4 +852,20 @@
             return super.getPackageOrSharedUser(appId);
         }
     }
+
+    @Nullable
+    @Override
+    public SharedUserApi getSharedUser(int sharedUserAppId) {
+        synchronized (mLock) {
+            return super.getSharedUser(sharedUserAppId);
+        }
+    }
+
+    @NonNull
+    @Override
+    public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
+        synchronized (mLock) {
+            return super.getSharedUserPackages(sharedUserAppId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index 24c08d1..72e67da 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -1283,4 +1283,20 @@
             return current.mComputer.getPackageOrSharedUser(appId);
         }
     }
+
+    @Nullable
+    @Override
+    public SharedUserApi getSharedUser(int sharedUserAppId) {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getSharedUser(sharedUserAppId);
+        }
+    }
+
+    @NonNull
+    @Override
+    public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getSharedUserPackages(sharedUserAppId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 3fb4ab1..3220b31 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -467,13 +467,14 @@
     private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
             PackageRemovedInfo outInfo, int flags) {
         final AndroidPackage pkg;
+        final SharedUserSetting sus;
         synchronized (mPm.mLock) {
             pkg = mPm.mPackages.get(ps.getPackageName());
+            sus = mPm.mSettings.getSharedUserSettingLPr(ps);
         }
 
         mAppDataHelper.destroyAppProfilesLIF(pkg);
 
-        final SharedUserSetting sus = ps.getSharedUser();
         final List<AndroidPackage> sharedUserPkgs =
                 sus != null ? sus.getPackages() : Collections.emptyList();
         final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index c418a10..684e6ba 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -86,7 +86,6 @@
 import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
 import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
-import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -254,15 +253,21 @@
         final String realPkgName = request.mRealPkgName;
         final List<String> changedAbiCodePath = result.mChangedAbiCodePath;
         final PackageSetting pkgSetting;
-        if (request.mPkgSetting != null && request.mPkgSetting.getSharedUser() != null
-                && request.mPkgSetting.getSharedUser() != result.mPkgSetting.getSharedUser()) {
-            // shared user changed, remove from old shared user
-            final SharedUserSetting sus = request.mPkgSetting.getSharedUser();
-            sus.removePackage(request.mPkgSetting);
-            // Prune unused SharedUserSetting
-            if (mPm.mSettings.checkAndPruneSharedUserLPw(sus, false)) {
-                // Set the app ID in removed info for UID_REMOVED broadcasts
-                reconciledPkg.mInstallResult.mRemovedInfo.mRemovedAppId = sus.userId;
+        if (request.mPkgSetting != null) {
+            SharedUserSetting requestSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(
+                    request.mPkgSetting);
+            SharedUserSetting resultSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(
+                    result.mPkgSetting);
+            if (requestSharedUserSetting != null
+                    && requestSharedUserSetting != resultSharedUserSetting) {
+                // shared user changed, remove from old shared user
+                requestSharedUserSetting.removePackage(request.mPkgSetting);
+                // Prune unused SharedUserSetting
+                if (mPm.mSettings.checkAndPruneSharedUserLPw(requestSharedUserSetting, false)) {
+                    // Set the app ID in removed info for UID_REMOVED broadcasts
+                    reconciledPkg.mInstallResult.mRemovedInfo.mRemovedAppId =
+                            requestSharedUserSetting.mAppId;
+                }
             }
         }
         if (result.mExistingSettingCopied) {
@@ -279,8 +284,9 @@
                 mPm.mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName());
             }
         }
-        if (pkgSetting.getSharedUser() != null) {
-            pkgSetting.getSharedUser().addPackage(pkgSetting);
+        SharedUserSetting sharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(pkgSetting);
+        if (sharedUserSetting != null) {
+            sharedUserSetting.addPackage(pkgSetting);
         }
         if (reconciledPkg.mInstallArgs != null
                 && reconciledPkg.mInstallArgs.mForceQueryableOverride) {
@@ -327,8 +333,8 @@
             ksms.removeAppKeySetDataLPw(pkg.getPackageName());
         }
         if (reconciledPkg.mSharedUserSignaturesChanged) {
-            pkgSetting.getSharedUser().signaturesChanged = Boolean.TRUE;
-            pkgSetting.getSharedUser().signatures.mSigningDetails = reconciledPkg.mSigningDetails;
+            sharedUserSetting.signaturesChanged = Boolean.TRUE;
+            sharedUserSetting.signatures.mSigningDetails = reconciledPkg.mSigningDetails;
         }
         pkgSetting.setSigningDetails(reconciledPkg.mSigningDetails);
 
@@ -963,7 +969,7 @@
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
                     reconciledPackages = ReconcilePackageUtils.reconcilePackages(
                             reconcileRequest, mSharedLibraries,
-                            mPm.mSettings.getKeySetManagerService());
+                            mPm.mSettings.getKeySetManagerService(), mPm.mSettings);
                 } catch (ReconcileFailure e) {
                     for (InstallRequest request : requests) {
                         request.mInstallResult.setError("Reconciliation failed...", e);
@@ -1242,7 +1248,10 @@
                 // we'll check this again later when scanning, but we want to
                 // bail early here before tripping over redefined permissions.
                 final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
-                if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+                final SharedUserSetting signatureCheckSus = mPm.mSettings.getSharedUserSettingLPr(
+                        signatureCheckPs);
+                if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, signatureCheckSus,
+                        scanFlags)) {
                     if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
                         throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                                 + parsedPackage.getPackageName() + " upgrade keys do not match the "
@@ -1257,7 +1266,9 @@
                                 ReconcilePackageUtils.isRecoverSignatureUpdateNeeded(
                                         mPm.getSettingsVersionForPackage(parsedPackage));
                         // We don't care about disabledPkgSetting on install for now.
-                        final boolean compatMatch = verifySignatures(signatureCheckPs, null,
+                        final boolean compatMatch =
+                                PackageManagerServiceUtils.verifySignatures(signatureCheckPs,
+                                        signatureCheckSus, null,
                                 parsedPackage.getSigningDetails(), compareCompat, compareRecover,
                                 isRollback);
                         // The new KeySets will be re-added later in the scanning process.
@@ -1496,6 +1507,7 @@
             int targetParseFlags = parseFlags;
             final PackageSetting ps;
             final PackageSetting disabledPs;
+            final SharedUserSetting sharedUserSetting;
             if (replace) {
                 final String pkgName11 = parsedPackage.getPackageName();
                 synchronized (mPm.mLock) {
@@ -1527,10 +1539,11 @@
 
                     ps = mPm.mSettings.getPackageLPr(pkgName11);
                     disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps);
+                    sharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(ps);
 
                     // verify signatures are valid
                     final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
-                    if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
+                    if (ksms.shouldCheckUpgradeKeySetLocked(ps, sharedUserSetting, scanFlags)) {
                         if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) {
                             throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                                     "New package not signed by keys specified by upgrade-keysets: "
@@ -1743,16 +1756,18 @@
 
         final PackageSetting sourcePackageSetting;
         final KeySetManagerService ksms;
+        final SharedUserSetting sharedUserSetting;
         synchronized (mPm.mLock) {
             sourcePackageSetting = mPm.mSettings.getPackageLPr(sourcePackageName);
             ksms = mPm.mSettings.getKeySetManagerService();
+            sharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(sourcePackageSetting);
         }
 
         final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
                 ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
         if (sourcePackageName.equals(parsedPackage.getPackageName())
                 && (ksms.shouldCheckUpgradeKeySetLocked(
-                sourcePackageSetting, scanFlags))) {
+                        sourcePackageSetting, sharedUserSetting, scanFlags))) {
             return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
         } else {
 
@@ -3574,7 +3589,8 @@
                                     mPm.getSettingsVersionForPackage(parsedPackage)));
                     final Map<String, ReconciledPackage> reconcileResult =
                             ReconcilePackageUtils.reconcilePackages(reconcileRequest,
-                                    mSharedLibraries, mPm.mSettings.getKeySetManagerService());
+                                    mSharedLibraries, mPm.mSettings.getKeySetManagerService(),
+                                    mPm.mSettings);
                     appIdCreated = optimisticallyRegisterAppId(scanResult);
                     commitReconciledScanResultLocked(reconcileResult.get(pkgName),
                             mPm.mUserManager.getUserIds());
@@ -3658,6 +3674,7 @@
         final PackageSetting installedPkgSetting;
         final PackageSetting originalPkgSetting;
         final SharedUserSetting sharedUserSetting;
+        SharedUserSetting oldSharedUserSetting = null;
 
         synchronized (mPm.mLock) {
             platformPackage = mPm.getPlatformPackage();
@@ -3683,17 +3700,21 @@
                     && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
                     && sharedUserSetting != null) {
                 Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
-                        + " (uid=" + sharedUserSetting.userId + "):"
+                        + " (uid=" + sharedUserSetting.mAppId + "):"
                         + " packages=" + sharedUserSetting.packages);
             }
+            if (installedPkgSetting != null) {
+                oldSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(installedPkgSetting);
+            }
         }
 
         final boolean isPlatformPackage = platformPackage != null
                 && platformPackage.getPackageName().equals(parsedPackage.getPackageName());
 
-        return new ScanRequest(parsedPackage, sharedUserSetting,
+        return new ScanRequest(parsedPackage, oldSharedUserSetting,
                 installedPkgSetting == null ? null : installedPkgSetting.getPkg() /* oldPkg */,
                 installedPkgSetting /* packageSetting */,
+                sharedUserSetting,
                 disabledPkgSetting /* disabledPackageSetting */,
                 originalPkgSetting  /* originalPkgSetting */,
                 realPkgName, parseFlags, scanFlags, isPlatformPackage, user, cpuAbiOverride);
@@ -3703,8 +3724,8 @@
     private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
             final @ParsingPackageUtils.ParseFlags int parseFlags,
             @PackageManagerService.ScanFlags int scanFlags, long currentTime,
-            @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
-
+            @Nullable UserHandle user, String cpuAbiOverride)
+            throws PackageManagerException {
         final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags,
                 scanFlags, user, cpuAbiOverride);
         final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting;
@@ -3725,8 +3746,9 @@
         synchronized (mPm.mLock) {
             assertPackageIsValid(parsedPackage, parseFlags, newScanFlags);
             final ScanRequest request = new ScanRequest(parsedPackage,
-                    initialScanRequest.mSharedUserSetting,
-                    initialScanRequest.mOldPkg, installedPkgSetting, disabledPkgSetting,
+                    initialScanRequest.mOldSharedUserSetting,
+                    initialScanRequest.mOldPkg, installedPkgSetting,
+                    initialScanRequest.mSharedUserSetting, disabledPkgSetting,
                     initialScanRequest.mOriginalPkgSetting, initialScanRequest.mRealPkgName,
                     parseFlags, scanFlags, initialScanRequest.mIsPlatformPackage, user,
                     cpuAbiOverride);
@@ -3775,8 +3797,9 @@
             if (scanSystemPartition && isSystemPkgUpdated) {
                 // we're updating the disabled package, so, scan it as the package setting
                 final ScanRequest request = new ScanRequest(parsedPackage,
-                        initialScanRequest.mSharedUserSetting,
+                        mPm.mSettings.getSharedUserSettingLPr(disabledPkgSetting),
                         null, disabledPkgSetting /* pkgSetting */,
+                        initialScanRequest.mSharedUserSetting,
                         null /* disabledPkgSetting */, null /* originalPkgSetting */,
                         null, parseFlags, scanFlags,
                         initialScanRequest.mIsPlatformPackage, user, null);
@@ -3994,12 +4017,14 @@
                 if (!verifyPackageUpdateLPr(originalPs, pkg)) {
                     // the new package is incompatible with the original
                     continue;
-                } else if (originalPs.getSharedUser() != null) {
-                    if (!originalPs.getSharedUser().name.equals(pkg.getSharedUserId())) {
+                } else if (mPm.mSettings.getSharedUserSettingLPr(originalPs) != null) {
+                    final String sharedUserSettingsName =
+                            mPm.mSettings.getSharedUserSettingLPr(originalPs).name;
+                    if (!sharedUserSettingsName.equals(pkg.getSharedUserId())) {
                         // the shared user id is incompatible with the original
                         Slog.w(TAG, "Unable to migrate data from " + originalPs.getPackageName()
-                                + " to " + pkg.getPackageName() + ": old uid "
-                                + originalPs.getSharedUser().name
+                                + " to " + pkg.getPackageName() + ": old shared user settings name "
+                                + sharedUserSettingsName
                                 + " differs from " + pkg.getSharedUserId());
                         continue;
                     }
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index afca350..5c29833 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -32,6 +32,7 @@
 
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
 import com.android.server.utils.WatchedArrayMap;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -354,9 +355,10 @@
         return mKeySets.get(id) != null;
     }
 
-    public boolean shouldCheckUpgradeKeySetLocked(PackageStateInternal oldPs, int scanFlags) {
+    public boolean shouldCheckUpgradeKeySetLocked(PackageStateInternal oldPs,
+            SharedUserApi sharedUserSetting, int scanFlags) {
         // Can't rotate keys during boot or if sharedUser.
-        if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || (oldPs.getSharedUser() != null)
+        if (oldPs == null || (scanFlags & SCAN_INITIAL) != 0 || (sharedUserSetting != null)
                 || !oldPs.getKeySetData().isUsingUpgradeKeySets()) {
             return false;
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ee5c638..6709bf1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1515,7 +1515,8 @@
                     return;
                 }
                 AndroidPackage pkg = packageState.getPkg();
-                SharedUserApi sharedUser = packageState.getSharedUser();
+                SharedUserApi sharedUser = m.mComputer.getSharedUser(
+                        packageState.getSharedUserAppId());
                 String oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, packageState);
 
                 if (pkg == null) {
@@ -7867,6 +7868,18 @@
                     migrateAppsData);
         }
 
+        @Override
+        @NonNull
+        public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
+            return PackageManagerService.this.mComputer.getSharedUserPackages(sharedUserAppId);
+        }
+
+        @Override
+        @Nullable
+        public SharedUserApi getSharedUserApi(int sharedUserAppId) {
+            return mComputer.getSharedUser(sharedUserAppId);
+        }
+
         @NonNull
         @Override
         public PackageStateMutator.InitialState recordInitialState() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index d6340b5..0dae12f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -505,6 +505,7 @@
      * @throws PackageManagerException if the signatures did not match.
      */
     public static boolean verifySignatures(PackageSetting pkgSetting,
+            @Nullable SharedUserSetting sharedUserSetting,
             PackageSetting disabledPkgSetting, SigningDetails parsedSignatures,
             boolean compareCompat, boolean compareRecover, boolean isRollback)
             throws PackageManagerException {
@@ -555,10 +556,8 @@
             }
         }
         // Check for shared user signatures
-        if (pkgSetting.getSharedUser() != null
-                && pkgSetting.getSharedUser().signatures.mSigningDetails
-                        != SigningDetails.UNKNOWN) {
-
+        if (sharedUserSetting != null
+                && sharedUserSetting.getSigningDetails() != SigningDetails.UNKNOWN) {
             // Already existing package. Make sure signatures match.  In case of signing certificate
             // rotation, the packages with newer certs need to be ok with being sharedUserId with
             // the older ones.  We check to see if either the new package is signed by an older cert
@@ -566,32 +565,32 @@
             // with being sharedUser with the existing signing cert.
             boolean match =
                     parsedSignatures.checkCapability(
-                            pkgSetting.getSharedUser().signatures.mSigningDetails,
+                            sharedUserSetting.getSigningDetails(),
                             SigningDetails.CertCapabilities.SHARED_USER_ID)
-                    || pkgSetting.getSharedUser().signatures.mSigningDetails.checkCapability(
+                    || sharedUserSetting.getSigningDetails().checkCapability(
                             parsedSignatures,
                             SigningDetails.CertCapabilities.SHARED_USER_ID);
             // Special case: if the sharedUserId capability check failed it could be due to this
             // being the only package in the sharedUserId so far and the lineage being updated to
             // deny the sharedUserId capability of the previous key in the lineage.
-            if (!match && pkgSetting.getSharedUser().packages.size() == 1
-                    && pkgSetting.getSharedUser().packages
+            if (!match && sharedUserSetting.packages.size() == 1
+                    && sharedUserSetting.packages
                             .valueAt(0).getPackageName().equals(packageName)) {
                 match = true;
             }
             if (!match && compareCompat) {
                 match = matchSignaturesCompat(
-                        packageName, pkgSetting.getSharedUser().signatures, parsedSignatures);
+                        packageName, sharedUserSetting.signatures, parsedSignatures);
             }
             if (!match && compareRecover) {
                 match =
                         matchSignaturesRecover(packageName,
-                                pkgSetting.getSharedUser().signatures.mSigningDetails,
+                                sharedUserSetting.signatures.mSigningDetails,
                                 parsedSignatures,
                                 SigningDetails.CertCapabilities.SHARED_USER_ID)
                         || matchSignaturesRecover(packageName,
                                 parsedSignatures,
-                                pkgSetting.getSharedUser().signatures.mSigningDetails,
+                                sharedUserSetting.signatures.mSigningDetails,
                                 SigningDetails.CertCapabilities.SHARED_USER_ID);
                 compatMatch |= match;
             }
@@ -599,14 +598,14 @@
                 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
                         "Package " + packageName
                         + " has no signatures that match those in shared user "
-                        + pkgSetting.getSharedUser().name + "; ignoring!");
+                        + sharedUserSetting.name + "; ignoring!");
             }
             // It is possible that this package contains a lineage that blocks sharedUserId access
             // to an already installed package in the sharedUserId signed with a previous key.
             // Iterate over all of the packages in the sharedUserId and ensure any that are signed
             // with a key in this package's lineage have the SHARED_USER_ID capability granted.
             if (parsedSignatures.hasPastSigningCertificates()) {
-                for (PackageSetting shUidPkgSetting : pkgSetting.getSharedUser().packages) {
+                for (PackageSetting shUidPkgSetting : sharedUserSetting.packages) {
                     // if the current package in the sharedUserId is the package being updated then
                     // skip this check as the update may revoke the sharedUserId capability from
                     // the key with which this app was previously signed.
@@ -633,7 +632,7 @@
             // If the lineage of this package diverges from the lineage of the sharedUserId then
             // do not allow the installation to proceed.
             if (!parsedSignatures.hasCommonAncestor(
-                    pkgSetting.getSharedUser().signatures.mSigningDetails)) {
+                    sharedUserSetting.signatures.mSigningDetails)) {
                 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
                         "Package " + packageName + " has a signing lineage "
                                 + "that diverges from the lineage of the sharedUserId");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index bd32d03..6efc55a 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -76,12 +76,9 @@
 public class PackageSetting extends SettingBase implements PackageStateInternal {
 
     /**
-     * Temporary holding space for the shared user ID. While parsing package settings, the
-     * shared users tag may come after the packages. In this case, we must delay linking the
-     * shared user setting with the package setting. The shared user ID lets us link the
-     * two objects.
+     * The shared user ID lets us link this object to {@link SharedUserSetting}.
      */
-    private int sharedUserId;
+    private int mSharedUserAppId;
 
     @Nullable
     private Map<String, Set<String>> mimeGroups;
@@ -130,13 +127,6 @@
     @Nullable
     private AndroidPackage pkg;
 
-    /**
-     * WARNING. The object reference is important. We perform integer equality and NOT
-     * object equality to check whether shared user settings are the same.
-     */
-    @Nullable
-    private SharedUserSetting sharedUser;
-
     /** @see AndroidPackage#getPath() */
     @NonNull
     private File mPath;
@@ -210,7 +200,7 @@
             String legacyNativeLibraryPath, String primaryCpuAbi,
             String secondaryCpuAbi, String cpuAbiOverride,
             long longVersionCode, int pkgFlags, int pkgPrivateFlags,
-            int sharedUserId,
+            int sharedUserAppId,
             String[] usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor,
             String[] usesStaticLibraries, long[] usesStaticLibrariesVersions,
             Map<String, Set<String>> mimeGroups,
@@ -231,7 +221,7 @@
         this.versionCode = longVersionCode;
         this.signatures = new PackageSignatures();
         this.installSource = InstallSource.EMPTY;
-        this.sharedUserId = sharedUserId;
+        this.mSharedUserAppId = sharedUserAppId;
         mDomainSetId = domainSetId;
         copyMimeGroups(mimeGroups);
         mSnapshot = makeCache();
@@ -261,7 +251,6 @@
         super(original);
         copyPackageSetting(original);
         if (sealedSnapshot) {
-            sharedUser = sharedUser == null ? null : sharedUser.snapshot();
             mSnapshot = new SnapshotCache.Sealed();
         } else {
             mSnapshot = makeCache();
@@ -313,10 +302,6 @@
         proto.end(packageToken);
     }
 
-    public boolean isSharedUser() {
-        return sharedUser != null;
-    }
-
     public PackageSetting setAppId(int appId) {
         this.mAppId = appId;
         onChanged();
@@ -489,11 +474,18 @@
         return this;
     }
 
-    public int getSharedUserIdInt() {
-        if (sharedUser != null) {
-            return sharedUser.userId;
-        }
-        return sharedUserId;
+    public void setSharedUserAppId(int sharedUserAppId) {
+        mSharedUserAppId = sharedUserAppId;
+    }
+
+    @Override
+    public int getSharedUserAppId() {
+        return mSharedUserAppId;
+    }
+
+    @Override
+    public boolean hasSharedUser() {
+        return mSharedUserAppId > 0;
     }
 
     @Override
@@ -564,9 +556,7 @@
     @Deprecated
     @Override
     public LegacyPermissionState getLegacyPermissionState() {
-        return (sharedUser != null)
-                ? sharedUser.getLegacyPermissionState()
-                : super.getLegacyPermissionState();
+        return super.getLegacyPermissionState();
     }
 
     public PackageSetting setInstallPermissionsFixed(boolean installPermissionsFixed) {
@@ -620,14 +610,13 @@
 
     public void copyPackageSetting(PackageSetting other) {
         super.copySettingBase(other);
-        sharedUserId = other.sharedUserId;
+        mSharedUserAppId = other.mSharedUserAppId;
         mLoadingProgress = other.mLoadingProgress;
         legacyNativeLibraryPath = other.legacyNativeLibraryPath;
         mName = other.mName;
         mRealName = other.mRealName;
         mAppId = other.mAppId;
         pkg = other.pkg;
-        sharedUser = other.sharedUser;
         mPath = other.mPath;
         mPathString = other.mPathString;
         mPrimaryCpuAbi = other.mPrimaryCpuAbi;
@@ -1168,12 +1157,6 @@
         return getPkg();
     }
 
-    @Nullable
-    @Override
-    public int getSharedUserId() {
-        return sharedUser == null ? -1 : sharedUser.userId;
-    }
-
     @NonNull
     public SigningInfo getSigningInfo() {
         return new SigningInfo(signatures.mSigningDetails);
@@ -1238,12 +1221,6 @@
         return this;
     }
 
-    public PackageSetting setSharedUser(SharedUserSetting sharedUser) {
-        this.sharedUser = sharedUser;
-        onChanged();
-        return this;
-    }
-
     public PackageSetting setCategoryOverride(int categoryHint) {
         this.categoryOverride = categoryHint;
         onChanged();
@@ -1377,15 +1354,6 @@
     }
 
     /**
-     * WARNING. The object reference is important. We perform integer equality and NOT
-     * object equality to check whether shared user settings are the same.
-     */
-    @DataClass.Generated.Member
-    public @Nullable SharedUserSetting getSharedUser() {
-        return sharedUser;
-    }
-
-    /**
      * @see AndroidPackage#getPath()
      */
     @DataClass.Generated.Member
@@ -1488,10 +1456,10 @@
     }
 
     @DataClass.Generated(
-            time = 1643648635766L,
+            time = 1644270960923L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
-            inputSignatures = "private  int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate  boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate  boolean updateAvailable\nprivate  boolean forceQueryableOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  boolean isSharedUser()\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isAnyInstalled(int[])\n  int[] queryInstalledUsers(int[],boolean)\n  long getCeDataInode(int)\n  void setCeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n  android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n  void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n  com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override int getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+            inputSignatures = "private  int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate  boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate  boolean updateAvailable\nprivate  boolean forceQueryableOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  void setSharedUserAppId(int)\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprotected  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isAnyInstalled(int[])\n  int[] queryInstalledUsers(int[],boolean)\n  long getCeDataInode(int)\n  void setCeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n  android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n  void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n  com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index f3d88ed..0b69cd3 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -22,19 +22,18 @@
 import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
 import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
 import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
-import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
 
 import android.content.pm.PackageManager;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.utils.WatchedLongSparseArray;
 
 import java.util.List;
@@ -43,7 +42,7 @@
 final class ReconcilePackageUtils {
     public static Map<String, ReconciledPackage> reconcilePackages(
             final ReconcileRequest request, SharedLibrariesImpl sharedLibraries,
-            KeySetManagerService ksms)
+            KeySetManagerService ksms, Settings settings)
             throws ReconcileFailure {
         final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
 
@@ -121,7 +120,10 @@
             boolean removeAppKeySetData = false;
             boolean sharedUserSignaturesChanged = false;
             SigningDetails signingDetails = null;
-            if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+            SharedUserSetting sharedUserSetting = settings.getSharedUserSettingLPr(
+                    signatureCheckPs);
+            if (ksms.shouldCheckUpgradeKeySetLocked(
+                    signatureCheckPs, sharedUserSetting, scanFlags)) {
                 if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
                     // We just determined the app is signed correctly, so bring
                     // over the latest parsed certs.
@@ -139,6 +141,7 @@
                 }
                 signingDetails = parsedPackage.getSigningDetails();
             } else {
+
                 try {
                     final Settings.VersionInfo versionInfo =
                             request.mVersionInfos.get(installPackageName);
@@ -146,9 +149,11 @@
                     final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
                     final boolean isRollback = installArgs != null
                             && installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
-                    final boolean compatMatch = verifySignatures(signatureCheckPs,
-                            disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
-                            compareRecover, isRollback);
+                    final boolean compatMatch =
+                            PackageManagerServiceUtils.verifySignatures(signatureCheckPs,
+                                    sharedUserSetting, disabledPkgSetting,
+                                    parsedPackage.getSigningDetails(), compareCompat,
+                                    compareRecover, isRollback);
                     // The new KeySets will be re-added later in the scanning process.
                     if (compatMatch) {
                         removeAppKeySetData = true;
@@ -161,21 +166,21 @@
                     // newer
                     // signing certificate than the existing one, and if so, copy over the new
                     // details
-                    if (signatureCheckPs.getSharedUser() != null) {
+                    if (sharedUserSetting != null) {
                         // Attempt to merge the existing lineage for the shared SigningDetails with
                         // the lineage of the new package; if the shared SigningDetails are not
                         // returned this indicates the new package added new signers to the lineage
                         // and/or changed the capabilities of existing signers in the lineage.
                         SigningDetails sharedSigningDetails =
-                                signatureCheckPs.getSharedUser().signatures.mSigningDetails;
+                                sharedUserSetting.signatures.mSigningDetails;
                         SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
                                 signingDetails);
                         if (mergedDetails != sharedSigningDetails) {
-                            signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+                            sharedUserSetting.signatures.mSigningDetails =
                                     mergedDetails;
                         }
-                        if (signatureCheckPs.getSharedUser().signaturesChanged == null) {
-                            signatureCheckPs.getSharedUser().signaturesChanged = Boolean.FALSE;
+                        if (sharedUserSetting.signaturesChanged == null) {
+                            sharedUserSetting.signaturesChanged = Boolean.FALSE;
                         }
                     }
                 } catch (PackageManagerException e) {
@@ -192,10 +197,10 @@
                     // updating
                     // the signatures on the first package scanned for the shared user (i.e. if the
                     // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
-                    if (signatureCheckPs.getSharedUser() != null) {
-                        final Signature[] sharedUserSignatures = signatureCheckPs.getSharedUser()
+                    if (sharedUserSetting != null) {
+                        final Signature[] sharedUserSignatures = sharedUserSetting
                                 .signatures.mSigningDetails.getSignatures();
-                        if (signatureCheckPs.getSharedUser().signaturesChanged != null
+                        if (sharedUserSetting.signaturesChanged != null
                                 && compareSignatures(sharedUserSignatures,
                                 parsedPackage.getSigningDetails().getSignatures())
                                 != PackageManager.SIGNATURE_MATCH) {
@@ -209,7 +214,7 @@
                                 throw new ReconcileFailure(
                                         INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
                                         "Signature mismatch for shared user: "
-                                                + scanResult.mPkgSetting.getSharedUser());
+                                                + sharedUserSetting);
                             } else {
                                 // Treat mismatched signatures on system packages using a shared
                                 // UID as
@@ -219,14 +224,14 @@
                                         "Signature mismatch on system package "
                                                 + parsedPackage.getPackageName()
                                                 + " for shared user "
-                                                + scanResult.mPkgSetting.getSharedUser());
+                                                + sharedUserSetting);
                             }
                         }
 
                         sharedUserSignaturesChanged = true;
-                        signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+                        sharedUserSetting.signatures.mSigningDetails =
                                 parsedPackage.getSigningDetails();
-                        signatureCheckPs.getSharedUser().signaturesChanged = Boolean.TRUE;
+                        sharedUserSetting.signaturesChanged = Boolean.TRUE;
                     }
                     // File a report about this.
                     String msg = "System package " + parsedPackage.getPackageName()
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 7e898cb..079903e 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -279,7 +279,7 @@
                 if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) {
                     // If we don't have a disabled system package to reinstall, the package is
                     // really gone and its permission state should be removed.
-                    final SharedUserSetting sus = deletedPs.getSharedUser();
+                    SharedUserSetting sus = mPm.mSettings.getSharedUserSettingLPr(deletedPs);
                     List<AndroidPackage> sharedUserPkgs =
                             sus != null ? sus.getPackages() : Collections.emptyList();
                     mPermissionManager.onPackageUninstalled(packageName, deletedPs.getAppId(),
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 79ab563..99d9d58 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -125,6 +125,7 @@
         final @ParsingPackageUtils.ParseFlags int parseFlags = request.mParseFlags;
         final @PackageManagerService.ScanFlags int scanFlags = request.mScanFlags;
         final String realPkgName = request.mRealPkgName;
+        final SharedUserSetting oldSharedUserSetting = request.mOldSharedUserSetting;
         final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
         final UserHandle user = request.mUser;
         final boolean isPlatformPackage = request.mIsPlatformPackage;
@@ -165,18 +166,18 @@
 
         int previousAppId = Process.INVALID_UID;
 
-        if (pkgSetting != null && pkgSetting.getSharedUser() != sharedUserSetting) {
-            if (pkgSetting.getSharedUser() != null && sharedUserSetting == null) {
+        if (pkgSetting != null && oldSharedUserSetting != sharedUserSetting) {
+            if (oldSharedUserSetting != null && sharedUserSetting == null) {
                 previousAppId = pkgSetting.getAppId();
                 // Log that something is leaving shareduid and keep going
                 Slog.i(TAG,
                         "Package " + parsedPackage.getPackageName() + " shared user changed from "
-                                + pkgSetting.getSharedUser().name + " to " + "<nothing>.");
+                                + oldSharedUserSetting.name + " to " + "<nothing>.");
             } else {
                 PackageManagerService.reportSettingsProblem(Log.WARN,
                         "Package " + parsedPackage.getPackageName() + " shared user changed from "
-                                + (pkgSetting.getSharedUser() != null
-                                ? pkgSetting.getSharedUser().name : "<nothing>")
+                                + (oldSharedUserSetting != null
+                                ? oldSharedUserSetting.name : "<nothing>")
                                 + " to "
                                 + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
                                 + "; replacing with new");
@@ -234,8 +235,8 @@
             // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
             // secondaryCpuAbi are not known at this point so we always update them
             // to null here, only to reset them at a later point.
-            Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
-                    destCodeFile, parsedPackage.getNativeLibraryDir(),
+            Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, oldSharedUserSetting,
+                    sharedUserSetting, destCodeFile, parsedPackage.getNativeLibraryDir(),
                     AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
                     AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
                     PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
@@ -390,16 +391,16 @@
                     + " abiOverride=" + pkgSetting.getCpuAbiOverride());
         }
 
-        if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.getSharedUser() != null) {
+        if ((scanFlags & SCAN_BOOTING) == 0 && oldSharedUserSetting != null) {
             // We don't do this here during boot because we can do it all
             // at once after scanning all existing packages.
             //
             // We also do this *before* we perform dexopt on this package, so that
             // we can avoid redundant dexopts, and also to make sure we've got the
             // code and package path correct.
-            changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.getSharedUser(),
+            changedAbiCodePath = applyAdjustedAbiToSharedUser(oldSharedUserSetting,
                     parsedPackage, packageAbiHelper.getAdjustedAbiForSharedUser(
-                            pkgSetting.getSharedUser().packages, parsedPackage));
+                            oldSharedUserSetting.packages, parsedPackage));
         }
 
         parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
diff --git a/services/core/java/com/android/server/pm/ScanRequest.java b/services/core/java/com/android/server/pm/ScanRequest.java
index 34abdb1..98d11bd 100644
--- a/services/core/java/com/android/server/pm/ScanRequest.java
+++ b/services/core/java/com/android/server/pm/ScanRequest.java
@@ -32,14 +32,16 @@
     @NonNull public final ParsedPackage mParsedPackage;
     /** The package this package replaces */
     @Nullable public final AndroidPackage mOldPkg;
-    /** Shared user settings, if the package has a shared user */
-    @Nullable public final SharedUserSetting mSharedUserSetting;
+    /** Shared user settings, if the old package has a shared user */
+    @Nullable public final SharedUserSetting mOldSharedUserSetting;
     /**
      * Package settings of the currently installed version.
      * <p><em>IMPORTANT:</em> The contents of this object may be modified
      * during scan.
      */
     @Nullable public final PackageSetting mPkgSetting;
+    /** Shared user settings of the currently installed package */
+    @Nullable public final SharedUserSetting mSharedUserSetting;
     /** A copy of the settings for the currently installed version */
     @Nullable public final PackageSetting mOldPkgSetting;
     /** Package settings for the disabled version on the /system partition */
@@ -59,9 +61,10 @@
 
     ScanRequest(
             @NonNull ParsedPackage parsedPackage,
-            @Nullable SharedUserSetting sharedUserSetting,
+            @Nullable SharedUserSetting oldSharedUserSetting,
             @Nullable AndroidPackage oldPkg,
             @Nullable PackageSetting pkgSetting,
+            @Nullable SharedUserSetting sharedUserSetting,
             @Nullable PackageSetting disabledPkgSetting,
             @Nullable PackageSetting originalPkgSetting,
             @Nullable String realPkgName,
@@ -73,6 +76,7 @@
         mParsedPackage = parsedPackage;
         mOldPkg = oldPkg;
         mPkgSetting = pkgSetting;
+        mOldSharedUserSetting = oldSharedUserSetting;
         mSharedUserSetting = sharedUserSetting;
         mOldPkgSetting = pkgSetting == null ? null : new PackageSetting(pkgSetting);
         mDisabledPkgSetting = disabledPkgSetting;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 0575b8c..53d0a73 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -780,13 +780,13 @@
         SharedUserSetting s = mSharedUsers.get(name);
         if (s == null && create) {
             s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
-            s.userId = acquireAndRegisterNewAppIdLPw(s);
-            if (s.userId < 0) {
+            s.mAppId = acquireAndRegisterNewAppIdLPw(s);
+            if (s.mAppId < 0) {
                 // < 0 means we couldn't assign a userid; throw exception
                 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                         "Creating shared user " + name + " failed");
             }
-            Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.userId);
+            Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.mAppId);
             mSharedUsers.put(name, s);
         }
         return s;
@@ -818,8 +818,9 @@
                 disabled = p;
             }
             mDisabledSysPackages.put(name, disabled);
-            if (disabled.getSharedUser() != null) {
-                disabled.getSharedUser().mDisabledPackages.add(disabled);
+            SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(disabled);
+            if (sharedUserSetting != null) {
+                sharedUserSetting.mDisabledPackages.add(disabled);
             }
             return true;
         }
@@ -832,8 +833,9 @@
             Log.w(PackageManagerService.TAG, "Package " + name + " is not disabled");
             return null;
         }
-        if (p.getSharedUser() != null) {
-            p.getSharedUser().mDisabledPackages.remove(p);
+        SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p);
+        if (sharedUserSetting != null) {
+            sharedUserSetting.mDisabledPackages.remove(p);
         }
         p.getPkgState().setUpdatedSystemApp(false);
         PackageSetting ret = addPackageLPw(name, p.getRealName(), p.getPath(),
@@ -856,9 +858,12 @@
 
     void removeDisabledSystemPackageLPw(String name) {
         final PackageSetting p = mDisabledSysPackages.remove(name);
-        if (p != null && p.getSharedUser() != null) {
-            p.getSharedUser().mDisabledPackages.remove(p);
-            checkAndPruneSharedUserLPw(p.getSharedUser(), false);
+        if (p != null) {
+            SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p);
+            if (sharedUserSetting != null) {
+                sharedUserSetting.mDisabledPackages.remove(p);
+                checkAndPruneSharedUserLPw(sharedUserSetting, false);
+            }
         }
     }
 
@@ -893,7 +898,7 @@
     SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
         SharedUserSetting s = mSharedUsers.get(name);
         if (s != null) {
-            if (s.userId == uid) {
+            if (s.mAppId == uid) {
                 return s;
             }
             PackageManagerService.reportSettingsProblem(Log.ERROR,
@@ -901,7 +906,7 @@
             return null;
         }
         s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
-        s.userId = uid;
+        s.mAppId = uid;
         if (registerExistingAppIdLPw(uid, s, name)) {
             mSharedUsers.put(name, s);
             return s;
@@ -974,7 +979,9 @@
                     usesStaticLibraries, usesStaticLibrariesVersions,
                     createMimeGroups(mimeGroupNames), domainSetId);
             pkgSetting.setLastModifiedTime(codePath.lastModified());
-            pkgSetting.setSharedUser(sharedUser);
+            if (sharedUser != null) {
+                pkgSetting.setSharedUserAppId(sharedUser.mAppId);
+            }
             // If this is not a system app, it starts out stopped.
             if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
                 if (DEBUG_STOPPED) {
@@ -1025,7 +1032,7 @@
                 }
             }
             if (sharedUser != null) {
-                pkgSetting.setAppId(sharedUser.userId);
+                pkgSetting.setAppId(sharedUser.mAppId);
             } else {
                 // Clone the setting here for disabled system packages
                 if (disabledPkg != null) {
@@ -1069,7 +1076,9 @@
      * WARNING: The provided PackageSetting object may be mutated.
      */
     static void updatePackageSetting(@NonNull PackageSetting pkgSetting,
-            @Nullable PackageSetting disabledPkg, @Nullable SharedUserSetting sharedUser,
+            @Nullable PackageSetting disabledPkg,
+            @Nullable SharedUserSetting existingSharedUserSetting,
+            @Nullable SharedUserSetting sharedUser,
             @NonNull File codePath, @Nullable String legacyNativeLibraryPath,
             @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags,
             int pkgPrivateFlags, @NonNull UserManagerService userManager,
@@ -1078,16 +1087,18 @@
             @Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId)
                     throws PackageManagerException {
         final String pkgName = pkgSetting.getPackageName();
-        if (!Objects.equals(pkgSetting.getSharedUser(), sharedUser) && sharedUser != null) {
-            PackageManagerService.reportSettingsProblem(Log.WARN,
-                    "Package " + pkgName + " shared user changed from "
-                    + (pkgSetting.getSharedUser() != null
-                                            ? pkgSetting.getSharedUser().name : "<nothing>")
-                    + " to " + sharedUser.name);
-            throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,
-                    "Updating application package " + pkgName + " failed");
+        if (sharedUser != null) {
+            if (!Objects.equals(existingSharedUserSetting, sharedUser)) {
+                PackageManagerService.reportSettingsProblem(Log.WARN,
+                        "Package " + pkgName + " shared user changed from "
+                                + (existingSharedUserSetting != null
+                                ? existingSharedUserSetting.name : "<nothing>")
+                                + " to " + sharedUser.name);
+                throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,
+                        "Updating application package " + pkgName + " failed");
+            }
+            pkgSetting.setSharedUserAppId(sharedUser.mAppId);
         }
-        pkgSetting.setSharedUser(sharedUser);
 
         if (!pkgSetting.getPath().equals(codePath)) {
             final boolean isSystem = pkgSetting.isSystem();
@@ -1228,11 +1239,13 @@
         }
         // If this app defines a shared user id initialize
         // the shared user signatures as well.
-        if (p.getSharedUser() != null
-                && p.getSharedUser().signatures.mSigningDetails.getSignatures() == null) {
-            p.getSharedUser().signatures.mSigningDetails = pkg.getSigningDetails();
+        SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p);
+        if (sharedUserSetting != null) {
+            if (sharedUserSetting.signatures.mSigningDetails.getSignatures() == null) {
+                sharedUserSetting.signatures.mSigningDetails = pkg.getSigningDetails();
+            }
         }
-        addPackageSettingLPw(p, p.getSharedUser());
+        addPackageSettingLPw(p, sharedUserSetting);
     }
 
     // Utility method that adds a PackageSetting to mPackages and
@@ -1241,23 +1254,24 @@
     private void addPackageSettingLPw(PackageSetting p, SharedUserSetting sharedUser) {
         mPackages.put(p.getPackageName(), p);
         if (sharedUser != null) {
-            if (p.getSharedUser() != null && p.getSharedUser() != sharedUser) {
+            SharedUserSetting existingSharedUserSetting = getSharedUserSettingLPr(p);
+            if (existingSharedUserSetting != null && existingSharedUserSetting != sharedUser) {
                 PackageManagerService.reportSettingsProblem(Log.ERROR,
                         "Package " + p.getPackageName() + " was user "
-                        + p.getSharedUser() + " but is now " + sharedUser
+                        + existingSharedUserSetting + " but is now " + sharedUser
                         + "; I am not changing its files so it will probably fail!");
-                p.getSharedUser().removePackage(p);
-            } else if (p.getAppId() != sharedUser.userId) {
+                sharedUser.removePackage(p);
+            } else if (p.getAppId() != sharedUser.mAppId) {
                 PackageManagerService.reportSettingsProblem(Log.ERROR,
-                    "Package " + p.getPackageName() + " was user id " + p.getAppId()
-                    + " but is now user " + sharedUser
-                    + " with id " + sharedUser.userId
-                    + "; I am not changing its files so it will probably fail!");
+                        "Package " + p.getPackageName() + " was user id " + p.getAppId()
+                                + " but is now user " + sharedUser
+                                + " with id " + sharedUser.mAppId
+                                + "; I am not changing its files so it will probably fail!");
             }
 
             sharedUser.addPackage(p);
-            p.setSharedUser(sharedUser);
-            p.setAppId(sharedUser.userId);
+            p.setSharedUserAppId(sharedUser.mAppId);
+            p.setAppId(sharedUser.mAppId);
         }
 
         // If the we know about this user id, we have to update it as it
@@ -1277,7 +1291,7 @@
     boolean checkAndPruneSharedUserLPw(SharedUserSetting s, boolean skipCheck) {
         if (skipCheck || (s.packages.isEmpty() && s.mDisabledPackages.isEmpty())) {
             if (mSharedUsers.remove(s.name) != null) {
-                removeAppIdLPw(s.userId);
+                removeAppIdLPw(s.mAppId);
                 return true;
             }
         }
@@ -1288,10 +1302,11 @@
         final PackageSetting p = mPackages.remove(name);
         if (p != null) {
             removeInstallerPackageStatus(name);
-            if (p.getSharedUser() != null) {
-                p.getSharedUser().removePackage(p);
-                if (checkAndPruneSharedUserLPw(p.getSharedUser(), false)) {
-                    return p.getSharedUser().userId;
+            SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p);
+            if (sharedUserSetting != null) {
+                sharedUserSetting.removePackage(p);
+                if (checkAndPruneSharedUserLPw(sharedUserSetting, false)) {
+                    return sharedUserSetting.mAppId;
                 }
             } else {
                 removeAppIdLPw(p.getAppId());
@@ -2465,7 +2480,7 @@
             for (final SharedUserSetting usr : mSharedUsers.values()) {
                 serializer.startTag(null, "shared-user");
                 serializer.attribute(null, ATTR_NAME, usr.name);
-                serializer.attributeInt(null, "userId", usr.userId);
+                serializer.attributeInt(null, "userId", usr.mAppId);
                 usr.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage());
                 serializer.endTag(null, "shared-user");
             }
@@ -2784,7 +2799,7 @@
             serializer.attribute(null, "cpuAbiOverride", pkg.getCpuAbiOverride());
         }
 
-        if (pkg.getSharedUser() == null) {
+        if (!pkg.hasSharedUser()) {
             serializer.attributeInt(null, "userId", pkg.getAppId());
         } else {
             serializer.attributeInt(null, "sharedUserId", pkg.getAppId());
@@ -2827,7 +2842,7 @@
         serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
         serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
         serializer.attributeLong(null, "version", pkg.getVersionCode());
-        if (pkg.getSharedUser() == null) {
+        if (!pkg.hasSharedUser()) {
             serializer.attributeInt(null, "userId", pkg.getAppId());
         } else {
             serializer.attributeInt(null, "sharedUserId", pkg.getAppId());
@@ -3099,21 +3114,22 @@
 
         for (int i = 0; i < N; i++) {
             final PackageSetting p = mPendingPackages.get(i);
-            final int sharedUserId = p.getSharedUserIdInt();
-            final Object idObj = getSettingLPr(sharedUserId);
+            final int sharedUserAppId = p.getSharedUserAppId();
+            if (sharedUserAppId <= 0) {
+                continue;
+            }
+            final Object idObj = getSettingLPr(sharedUserAppId);
             if (idObj instanceof SharedUserSetting) {
                 final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
-                p.setSharedUser(sharedUser);
-                p.setAppId(sharedUser.userId);
                 addPackageSettingLPw(p, sharedUser);
             } else if (idObj != null) {
                 String msg = "Bad package setting: package " + p.getPackageName()
-                        + " has shared uid " + sharedUserId + " that is not a shared uid\n";
+                        + " has shared uid " + sharedUserAppId + " that is not a shared uid\n";
                 mReadMessages.append(msg);
                 PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
             } else {
                 String msg = "Bad package setting: package " + p.getPackageName()
-                        + " has shared uid " + sharedUserId + " that is not defined\n";
+                        + " has shared uid " + sharedUserAppId + " that is not defined\n";
                 mReadMessages.append(msg);
                 PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
             }
@@ -3146,8 +3162,9 @@
         for (PackageSetting disabledPs : mDisabledSysPackages.values()) {
             final Object id = getSettingLPr(disabledPs.getAppId());
             if (id instanceof SharedUserSetting) {
-                disabledPs.setSharedUser((SharedUserSetting) id);
-                disabledPs.getSharedUser().mDisabledPackages.add(disabledPs);
+                SharedUserSetting sharedUserSetting = (SharedUserSetting) id;
+                sharedUserSetting.mDisabledPackages.add(disabledPs);
+                disabledPs.setSharedUserAppId(sharedUserSetting.mAppId);
             }
         }
 
@@ -3563,7 +3580,9 @@
         ps.setLastUpdateTime(parser.getAttributeLongHex(null, "ut", 0));
         ps.setAppId(parser.getAttributeInt(null, "userId", 0));
         if (ps.getAppId() <= 0) {
-            ps.setAppId(parser.getAttributeInt(null, "sharedUserId", 0));
+            final int sharedUserAppId = parser.getAttributeInt(null, "sharedUserId", 0);
+            ps.setAppId(sharedUserAppId);
+            ps.setSharedUserAppId(sharedUserAppId);
         }
         final float loadingProgress =
                 parser.getAttributeFloat(null, "loadingProgress", 0);
@@ -3578,7 +3597,13 @@
             }
 
             if (parser.getName().equals(TAG_PERMISSIONS)) {
-                readInstallPermissionsLPr(parser, ps.getLegacyPermissionState(), users);
+                final LegacyPermissionState legacyState;
+                if (ps.hasSharedUser()) {
+                    legacyState = getSettingLPr(ps.getSharedUserAppId()).getLegacyPermissionState();
+                } else {
+                    legacyState = ps.getLegacyPermissionState();
+                }
+                readInstallPermissionsLPr(parser, legacyState, users);
             } else if (parser.getName().equals(TAG_USES_STATIC_LIB)) {
                 readUsesStaticLibLPw(parser, ps);
             } else if (parser.getName().equals(TAG_USES_SDK_LIB)) {
@@ -3603,7 +3628,7 @@
         String name = null;
         String realName = null;
         int userId = 0;
-        int sharedUserId = 0;
+        int sharedUserAppId = 0;
         String codePathStr = null;
         String legacyCpuAbiString = null;
         String legacyNativeLibraryPathStr = null;
@@ -3635,7 +3660,7 @@
             name = parser.getAttributeValue(null, ATTR_NAME);
             realName = parser.getAttributeValue(null, "realName");
             userId = parser.getAttributeInt(null, "userId", 0);
-            sharedUserId = parser.getAttributeInt(null, "sharedUserId", 0);
+            sharedUserAppId = parser.getAttributeInt(null, "sharedUserId", 0);
             codePathStr = parser.getAttributeValue(null, "codePath");
 
             legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi");
@@ -3729,7 +3754,7 @@
             lastUpdateTime = parser.getAttributeLongHex(null, "ut", 0);
             if (PackageManagerService.DEBUG_SETTINGS)
                 Log.v(PackageManagerService.TAG, "Reading package: " + name + " userId=" + userId
-                        + " sharedUserId=" + sharedUserId);
+                        + " sharedUserId=" + sharedUserAppId);
             if (realName != null) {
                 realName = realName.intern();
             }
@@ -3759,12 +3784,12 @@
                     packageSetting.setLastModifiedTime(timeStamp);
                     packageSetting.setLastUpdateTime(lastUpdateTime);
                 }
-            } else if (sharedUserId != 0) {
-                if (sharedUserId > 0) {
+            } else if (sharedUserAppId != 0) {
+                if (sharedUserAppId > 0) {
                     packageSetting = new PackageSetting(name.intern(), realName,
                             new File(codePathStr), legacyNativeLibraryPathStr,
                             primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
-                            versionCode, pkgFlags, pkgPrivateFlags, sharedUserId,
+                            versionCode, pkgFlags, pkgPrivateFlags, sharedUserAppId,
                             null /* usesSdkLibraries */,
                             null /* usesSdkLibrariesVersions */,
                             null /* usesStaticLibraries */,
@@ -3775,11 +3800,11 @@
                     mPendingPackages.add(packageSetting);
                     if (PackageManagerService.DEBUG_SETTINGS)
                         Log.i(PackageManagerService.TAG, "Reading package " + name
-                                + ": sharedUserId=" + sharedUserId + " pkg=" + packageSetting);
+                                + ": sharedUserId=" + sharedUserAppId + " pkg=" + packageSetting);
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Error in package manager settings: package " + name
-                                    + " has bad sharedId " + sharedUserId + " at "
+                                    + " has bad sharedId " + sharedUserAppId + " at "
                                     + parser.getPositionDescription());
                 }
             } else {
@@ -3849,8 +3874,14 @@
                     packageSetting.getSignatures()
                             .readXml(parser,mPastSignatures.untrackedStorage());
                 } else if (tagName.equals(TAG_PERMISSIONS)) {
-                    readInstallPermissionsLPr(parser,
-                            packageSetting.getLegacyPermissionState(), users);
+                    final LegacyPermissionState legacyState;
+                    if (packageSetting.hasSharedUser()) {
+                        legacyState = getSettingLPr(
+                                packageSetting.getSharedUserAppId()).getLegacyPermissionState();
+                    } else {
+                        legacyState = packageSetting.getLegacyPermissionState();
+                    }
+                    readInstallPermissionsLPr(parser, legacyState, users);
                     packageSetting.setInstallPermissionsFixed(true);
                 } else if (tagName.equals("proper-signing-keyset")) {
                     long id = parser.getAttributeLong(null, "identifier");
@@ -4305,6 +4336,19 @@
         return pkg.getCurrentEnabledStateLPr(classNameStr, userId);
     }
 
+    SharedUserSetting getSharedUserSettingLPr(String packageName) {
+        final PackageSetting ps = mPackages.get(packageName);
+        return getSharedUserSettingLPr(ps);
+    }
+
+    @Nullable
+    SharedUserSetting getSharedUserSettingLPr(PackageSetting ps) {
+        if (ps == null || !ps.hasSharedUser()) {
+            return null;
+        }
+        return (SharedUserSetting) getSettingLPr(ps.getSharedUserAppId());
+    }
+
     /**
      * Returns all users on the device, including pre-created and dying users.
      *
@@ -4523,8 +4567,9 @@
 
         pw.print(prefix); pw.print("  userId="); pw.println(ps.getAppId());
 
-        if (ps.getSharedUser() != null) {
-            pw.print(prefix); pw.print("  sharedUser="); pw.println(ps.getSharedUser());
+        SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(ps);
+        if (sharedUserSetting != null) {
+            pw.print(prefix); pw.print("  sharedUser="); pw.println(sharedUserSetting);
         }
         pw.print(prefix); pw.print("  pkg="); pw.println(pkg);
         pw.print(prefix); pw.print("  codePath="); pw.println(ps.getPathString());
@@ -4808,7 +4853,7 @@
             }
         }
 
-        if (ps.getSharedUser() == null || permissionNames != null || dumpAll) {
+        if (!ps.hasSharedUser() || permissionNames != null || dumpAll) {
             dumpInstallPermissionsLPr(pw, prefix + "  ", permissionNames, permissionsState, users);
         }
 
@@ -4928,7 +4973,7 @@
                         pw.println(lastDisabledAppCaller);
             }
 
-            if (ps.getSharedUser() == null) {
+            if (!ps.hasSharedUser()) {
                 dumpGidsLPr(pw, prefix + "    ", mPermissionDataProvider.getGidsForUid(
                         UserHandle.getUid(user.id, ps.getAppId())));
                 dumpRuntimePermissionsLPr(pw, prefix + "    ", permissionNames, permissionsState
@@ -4981,7 +5026,7 @@
             }
 
             if (!checkin && packageName != null) {
-                dumpState.setSharedUser(ps.getSharedUser());
+                dumpState.setSharedUser(getSharedUserSettingLPr(ps));
             }
 
             if (!checkin && !printedSomething) {
@@ -5064,7 +5109,7 @@
                 continue;
             }
             final LegacyPermissionState permissionsState =
-                    mPermissionDataProvider.getLegacyPermissionState(su.userId);
+                    mPermissionDataProvider.getLegacyPermissionState(su.mAppId);
             if (permissionNames != null
                     && !permissionsState.hasPermissionState(permissionNames)) {
                 continue;
@@ -5084,7 +5129,7 @@
                 pw.println("):");
 
                 String prefix = "    ";
-                pw.print(prefix); pw.print("userId="); pw.println(su.userId);
+                pw.print(prefix); pw.print("userId="); pw.println(su.mAppId);
 
                 pw.print(prefix); pw.println("Packages");
                 final int numPackages = su.packages.size();
@@ -5108,7 +5153,7 @@
                 for (UserInfo user : users) {
                     final int userId = user.id;
                     final int[] gids = mPermissionDataProvider.getGidsForUid(UserHandle.getUid(
-                            userId, su.userId));
+                            userId, su.mAppId));
                     final Collection<PermissionState> permissions =
                             permissionsState.getPermissionStates(userId);
                     if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) {
@@ -5119,7 +5164,7 @@
                     }
                 }
             } else {
-                pw.print("suid,"); pw.print(su.userId); pw.print(","); pw.println(su.name);
+                pw.print("suid,"); pw.print(su.mAppId); pw.print(","); pw.println(su.name);
             }
         }
     }
@@ -5521,7 +5566,7 @@
                 for (int i = 0; i < packagesSize; i++) {
                     String packageName = packageStates.keyAt(i);
                     PackageStateInternal packageState = packageStates.valueAt(i);
-                    if (packageState.getSharedUser() == null) {
+                    if (!packageState.hasSharedUser()) {
                         List<RuntimePermissionsState.PermissionState> permissions =
                                 getPermissionsFromPermissionsState(
                                         packageState.getLegacyPermissionState(), userId);
@@ -5627,7 +5672,7 @@
                                 packageSetting.getLegacyPermissionState(),
                                 userId);
                         packageSetting.setInstallPermissionsFixed(true);
-                    } else if (packageSetting.getSharedUser() == null && !isUpgradeToR) {
+                    } else if (!packageSetting.hasSharedUser() && !isUpgradeToR) {
                         Slogf.w(TAG, "Missing permission state for package %s on user %d",
                                 packageName, userId);
                         packageSetting.getLegacyPermissionState().setMissing(true, userId);
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 5ef1471..d0e1e6c 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -19,9 +19,6 @@
 import android.annotation.NonNull;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.SigningDetails;
-import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProcessImpl;
 import android.service.pm.PackageServiceDumpProto;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -29,8 +26,12 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.permission.LegacyPermissionState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.component.ParsedProcessImpl;
 import com.android.server.utils.SnapshotCache;
 
 import libcore.util.EmptyArray;
@@ -46,7 +47,7 @@
 public final class SharedUserSetting extends SettingBase implements SharedUserApi {
     final String name;
 
-    int userId;
+    int mAppId;
 
     /** @see SharedUserApi#getUidFlags() **/
     int uidFlags;
@@ -98,7 +99,7 @@
     private SharedUserSetting(SharedUserSetting orig) {
         super(orig);
         name = orig.name;
-        userId = orig.userId;
+        mAppId = orig.mAppId;
         uidFlags = orig.uidFlags;
         uidPrivateFlags = orig.uidPrivateFlags;
         packages = new ArraySet<>(orig.packages);
@@ -133,12 +134,12 @@
     @Override
     public String toString() {
         return "SharedUserSetting{" + Integer.toHexString(System.identityHashCode(this)) + " "
-                + name + "/" + userId + "}";
+                + name + "/" + mAppId + "}";
     }
 
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
-        proto.write(PackageServiceDumpProto.SharedUserProto.UID, userId);
+        proto.write(PackageServiceDumpProto.SharedUserProto.UID, mAppId);
         proto.write(PackageServiceDumpProto.SharedUserProto.NAME, name);
         proto.end(token);
     }
@@ -286,7 +287,7 @@
     /** Updates all fields in this shared user setting from another. */
     public SharedUserSetting updateFrom(SharedUserSetting sharedUser) {
         super.copySettingBase(sharedUser);
-        this.userId = sharedUser.userId;
+        this.mAppId = sharedUser.mAppId;
         this.uidFlags = sharedUser.uidFlags;
         this.uidPrivateFlags = sharedUser.uidPrivateFlags;
         this.seInfoTargetSdkVersion = sharedUser.seInfoTargetSdkVersion;
@@ -315,8 +316,8 @@
     }
 
     @Override
-    public int getUserId() {
-        return userId;
+    public int getAppId() {
+        return mAppId;
     }
 
     @Override
@@ -363,4 +364,10 @@
     public ArrayMap<String, ParsedProcess> getProcesses() {
         return processes;
     }
+
+    @NonNull
+    @Override
+    public LegacyPermissionState getSharedUserLegacyPermissionState() {
+        return super.getLegacyPermissionState();
+    }
 }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e63d721..5c4d011 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1517,7 +1517,6 @@
         return userTypeDetails.getBadgeNoBackground();
     }
 
-    @Override
     public boolean isProfile(@UserIdInt int userId) {
         checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
         synchronized (mUsersLock) {
@@ -1526,21 +1525,19 @@
         }
     }
 
+    /**
+     * Returns the user type (if it is a profile), empty string (if it isn't a profile),
+     * or null (if the user doesn't exist).
+     */
     @Override
-    public boolean isManagedProfile(@UserIdInt int userId) {
-        checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile");
+    public @Nullable String getProfileType(@UserIdInt int userId) {
+        checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getProfileType");
         synchronized (mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
-            return userInfo != null && userInfo.isManagedProfile();
-        }
-    }
-
-    @Override
-    public boolean isCloneProfile(@UserIdInt int userId) {
-        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isCloneProfile");
-        synchronized (mUsersLock) {
-            UserInfo userInfo = getUserInfoLU(userId);
-            return userInfo != null && userInfo.isCloneProfile();
+            if (userInfo != null) {
+                return userInfo.isProfile() ? userInfo.userType : "";
+            }
+            return null;
         }
     }
 
@@ -5163,6 +5160,8 @@
                 nextId = scanNextAvailableIdLocked();
             }
         }
+        // If we got here, we probably recycled user ids, so invalidate any caches.
+        UserManager.invalidateStaticUserProperties();
         if (nextId < 0) {
             throw new IllegalStateException("No user id available!");
         }
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 6e6585e..1e3b67c 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -332,13 +332,13 @@
                 }
 
                 String typeName = parser.getAttributeValue(null, "name");
-                if (typeName == null) {
+                if (typeName == null || typeName.equals("")) {
                     Slog.w(LOG_TAG, "Skipping user type with no name in "
                             + parser.getPositionDescription());
                     XmlUtils.skipCurrentTag(parser);
                     continue;
                 }
-                typeName.intern();
+                typeName = typeName.intern();
 
                 UserTypeDetails.Builder builder;
                 if (typeName.startsWith("android.")) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index e9074c4..ed47bfb7 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -42,6 +42,7 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE;
 import static android.content.pm.PackageManager.MASK_PERMISSION_FLAGS_ALL;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.os.Process.INVALID_UID;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED;
 import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED;
@@ -130,6 +131,7 @@
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.AndroidPackageApi;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
 import com.android.server.pm.pkg.component.ParsedPermission;
@@ -2542,16 +2544,18 @@
                 if (uidState.isMissing()) {
                     Collection<String> uidRequestedPermissions;
                     int targetSdkVersion;
-                    if (ps.getSharedUser() == null) {
+                    if (!ps.hasSharedUser()) {
                         uidRequestedPermissions = pkg.getRequestedPermissions();
                         targetSdkVersion = pkg.getTargetSdkVersion();
                     } else {
                         uidRequestedPermissions = new ArraySet<>();
                         targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
-                        List<AndroidPackage> packages = ps.getSharedUser().getPackages();
+                        final ArraySet<PackageStateInternal> packages =
+                                mPackageManagerInt.getSharedUserPackages(ps.getSharedUserAppId());
                         int packagesSize = packages.size();
                         for (int i = 0; i < packagesSize; i++) {
-                            AndroidPackage sharedUserPackage = packages.get(i);
+                            AndroidPackageApi sharedUserPackage =
+                                    packages.valueAt(i).getAndroidPackage();
                             uidRequestedPermissions.addAll(
                                     sharedUserPackage.getRequestedPermissions());
                             targetSdkVersion = Math.min(targetSdkVersion,
@@ -2593,7 +2597,7 @@
 
                 if (replace) {
                     userState.setInstallPermissionsFixed(ps.getPackageName(), false);
-                    if (ps.getSharedUser() == null) {
+                    if (!ps.hasSharedUser()) {
                         origState = new UidPermissionState(uidState);
                         uidState.reset();
                     } else {
@@ -2603,7 +2607,8 @@
                         // changed runtime permissions here are promotion of an install to
                         // runtime and revocation of a runtime from a shared user.
                         if (revokeUnusedSharedUserPermissionsLocked(
-                                ps.getSharedUser().getPackages(), uidState)) {
+                                mPackageManagerInt.getSharedUserPackages(ps.getSharedUserAppId()),
+                                uidState)) {
                             updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
                             runtimePermissionsRevoked = true;
                         }
@@ -3170,44 +3175,12 @@
                     inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, newPerm, ps,
                             pkg);
                 }
-            } else if (NOTIFICATION_PERMISSIONS.contains(newPerm)) {
-                //&& (origPs.getPermissionState(newPerm) == null) {
-                // TODO(b/205888750): add back line about origPs once all TODO sections below are
-                //  propagated through droidfood
-                Permission bp = mRegistry.getPermission(newPerm);
-                if (bp == null) {
-                    throw new IllegalStateException("Unknown new permission " + newPerm);
-                }
-                if (!isUserSetOrPregrantedOrFixed(ps.getPermissionFlags(newPerm))) {
-                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
-                    int setFlag = ps.isPermissionGranted(newPerm)
-                            ? 0 : FLAG_PERMISSION_REVIEW_REQUIRED;
-                    ps.updatePermissionFlags(bp, FLAG_PERMISSION_REVIEW_REQUIRED, setFlag);
-                    // TODO(b/205888750): remove if/else block once propagated through droidfood
-                    if (ps.isPermissionGranted(newPerm)
-                            && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M) {
-                        ps.revokePermission(bp);
-                    } else if (!ps.isPermissionGranted(newPerm)
-                            && pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
-                        ps.grantPermission(bp);
-                    }
-                } else {
-                    // TODO(b/205888750): remove once propagated through droidfood
-                    ps.updatePermissionFlags(bp, FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
-                            | FLAG_PERMISSION_REVIEW_REQUIRED, 0);
-                }
             }
         }
 
         return updatedUserIds;
     }
 
-    private boolean isUserSetOrPregrantedOrFixed(int flags) {
-        return (flags & (FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED
-                | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED
-                | FLAG_PERMISSION_GRANTED_BY_DEFAULT | FLAG_PERMISSION_GRANTED_BY_ROLE)) != 0;
-    }
-
     @NonNull
     @Override
     public List<SplitPermissionInfoParcelable> getSplitPermissions() {
@@ -3849,13 +3822,14 @@
 
     @GuardedBy("mLock")
     private boolean revokeUnusedSharedUserPermissionsLocked(
-            List<AndroidPackage> pkgList, UidPermissionState uidState) {
+            ArraySet<PackageStateInternal> pkgList, UidPermissionState uidState) {
         // Collect all used permissions in the UID
         final ArraySet<String> usedPermissions = new ArraySet<>();
         if (pkgList == null || pkgList.size() == 0) {
             return false;
         }
-        for (AndroidPackage pkg : pkgList) {
+        for (PackageStateInternal pkgState : pkgList) {
+            final AndroidPackageApi pkg = pkgState.getAndroidPackage();
             if (pkg.getRequestedPermissions().isEmpty()) {
                 continue;
             }
@@ -4489,8 +4463,13 @@
         final int[] userIds = getAllUserIds();
         mPackageManagerInt.forEachPackageState(ps -> {
             final int appId = ps.getAppId();
-            final LegacyPermissionState legacyState = ps.getLegacyPermissionState();
-
+            final LegacyPermissionState legacyState;
+            if (ps.hasSharedUser()) {
+                legacyState = mPackageManagerInt.getSharedUserApi(
+                        ps.getSharedUserAppId()).getSharedUserLegacyPermissionState();
+            } else {
+                legacyState = ps.getLegacyPermissionState();
+            }
             synchronized (mLock) {
                 for (final int userId : userIds) {
                     final UserPermissionState userState = mState.getOrCreateUserState(userId);
@@ -4530,7 +4509,13 @@
         }
         mPackageManagerInt.forEachPackageSetting(ps -> {
             ps.setInstallPermissionsFixed(false);
-            final LegacyPermissionState legacyState = ps.getLegacyPermissionState();
+            final LegacyPermissionState legacyState;
+            if (ps.hasSharedUser()) {
+                legacyState = mPackageManagerInt.getSharedUserApi(
+                        ps.getSharedUserAppId()).getSharedUserLegacyPermissionState();
+            } else {
+                legacyState = ps.getLegacyPermissionState();
+            }
             legacyState.reset();
             final int appId = ps.getAppId();
 
@@ -4876,7 +4861,7 @@
         final PackageStateInternal ps =
                 mPackageManagerInt.getPackageStateInternal(pkg.getPackageName());
 
-        if (ps.getSharedUser() != null) {
+        if (ps.hasSharedUser()) {
             // The package is joining a shared user group. This can only happen when a system
             // app left shared UID with an update, and then the update is uninstalled.
             // If no apps remain in its original shared UID group, clone the current
@@ -4967,7 +4952,7 @@
     private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, int previousAppId,
             @NonNull PermissionManagerServiceInternal.PackageInstalledParams params,
             @UserIdInt int[] userIds) {
-        if (previousAppId != Process.INVALID_UID) {
+        if (previousAppId != INVALID_UID) {
             handleAppIdMigration(pkg, previousAppId);
         }
         updatePermissions(pkg.getPackageName(), pkg);
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index 9fa9644..7726d7f 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -159,12 +159,18 @@
     String getSecondaryCpuAbi();
 
     /**
-     * Retrieves the shared user ID. Note that the actual shared user data is not available here and
-     * must be queried separately.
-     *
-     * @return the shared user this package is a part of, or -1 if it's not part of a shared user.
+     * Whether the package shares the same user ID as other packages
      */
-    int getSharedUserId();
+    boolean hasSharedUser();
+
+    /**
+     * Retrieves the shared user app ID. Note that the actual shared user data is not available here
+     * and must be queried separately.
+     *
+     * @return the app ID of the shared user that this package is a part of, or -1 if it's not part
+     * of a shared user.
+     */
+    int getSharedUserAppId();
 
     @NonNull
     SigningInfo getSigningInfo();
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index 9395ca5..9a21e24 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -126,8 +126,8 @@
     private final String mPrimaryCpuAbi;
     @Nullable
     private final String mSecondaryCpuAbi;
-    @Nullable
-    private final int mSharedUserId;
+    private final boolean mHasSharedUser;
+    private final int mSharedUserAppId;
     @NonNull
     private final String[] mUsesSdkLibraries;
     @NonNull
@@ -172,7 +172,8 @@
         mPath = pkgState.getPath();
         mPrimaryCpuAbi = pkgState.getPrimaryCpuAbi();
         mSecondaryCpuAbi = pkgState.getSecondaryCpuAbi();
-        mSharedUserId = pkgState.getSharedUserId();
+        mHasSharedUser = pkgState.hasSharedUser();
+        mSharedUserAppId = pkgState.getSharedUserAppId();
         mUsesSdkLibraries = pkgState.getUsesSdkLibraries();
         mUsesSdkLibrariesVersionsMajor = pkgState.getUsesSdkLibrariesVersionsMajor();
         mUsesStaticLibraries = pkgState.getUsesStaticLibraries();
@@ -271,6 +272,15 @@
         return mLongVersionCode;
     }
 
+    @Override
+    public boolean hasSharedUser() {
+        return mHasSharedUser;
+    }
+
+    @Override
+    public int getSharedUserAppId() {
+        return mSharedUserAppId;
+    }
     /**
      * @hide
      */
@@ -516,7 +526,7 @@
         }
 
         @DataClass.Generated(
-                time = 1640209608883L,
+                time = 1644270981508L,
                 codegenVersion = "1.0.23",
                 sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
                 inputSignatures = "private  int mBooleans\nprivate final  long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final  int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final  long mFirstInstallTime\npublic static  com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final  int HIDDEN\nprivate static final  int INSTALLED\nprivate static final  int INSTANT_APP\nprivate static final  int NOT_LAUNCHED\nprivate static final  int STOPPED\nprivate static final  int SUSPENDED\nprivate static final  int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@@ -615,8 +625,8 @@
     }
 
     @DataClass.Generated.Member
-    public @Nullable int getSharedUserId() {
-        return mSharedUserId;
+    public boolean isHasSharedUser() {
+        return mHasSharedUser;
     }
 
     @DataClass.Generated.Member
@@ -671,10 +681,10 @@
     }
 
     @DataClass.Generated(
-            time = 1640209608912L,
+            time = 1644270981543L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
-            inputSignatures = "private  int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final  int mAppId\nprivate final  int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final  long mLastModifiedTime\nprivate final  long mLastUpdateTime\nprivate final  long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.Integer mSharedUserId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserState> mUserStates\npublic static  com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final  int SYSTEM\nprivate static final  int EXTERNAL_STORAGE\nprivate static final  int PRIVILEGED\nprivate static final  int OEM\nprivate static final  int VENDOR\nprivate static final  int PRODUCT\nprivate static final  int SYSTEM_EXT\nprivate static final  int REQUIRED_FOR_SYSTEM_USER\nprivate static final  int ODM\nprivate static final  int FORCE_QUERYABLE_OVERRIDE\nprivate static final  int HIDDEN_UNTIL_INSTALLED\nprivate static final  int INSTALL_PERMISSIONS_FIXED\nprivate static final  int UPDATE_AVAILABLE\nprivate static final  int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+            inputSignatures = "private  int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final  int mAppId\nprivate final  int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final  long mLastModifiedTime\nprivate final  long mLastUpdateTime\nprivate final  long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final  boolean mHasSharedUser\nprivate final  int mSharedUserAppId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserState> mUserStates\npublic static  com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override int getSharedUserAppId()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final  int SYSTEM\nprivate static final  int EXTERNAL_STORAGE\nprivate static final  int PRIVILEGED\nprivate static final  int OEM\nprivate static final  int VENDOR\nprivate static final  int PRODUCT\nprivate static final  int SYSTEM_EXT\nprivate static final  int REQUIRED_FOR_SYSTEM_USER\nprivate static final  int ODM\nprivate static final  int FORCE_QUERYABLE_OVERRIDE\nprivate static final  int HIDDEN_UNTIL_INSTALLED\nprivate static final  int INSTALL_PERMISSIONS_FIXED\nprivate static final  int UPDATE_AVAILABLE\nprivate static final  int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index fb2fe1f..68a00a9 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -50,9 +50,6 @@
     @NonNull
     InstallSource getInstallSource();
 
-    @Nullable
-    SharedUserApi getSharedUser();
-
     // TODO: Remove this in favor of boolean APIs
     int getFlags();
     int getPrivateFlags();
diff --git a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
index 43eac53..94a87f3 100644
--- a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
+++ b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
@@ -23,6 +23,7 @@
 import android.util.ArraySet;
 
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.permission.LegacyPermissionState;
 import com.android.server.pm.pkg.component.ParsedProcess;
 
 import java.util.List;
@@ -33,7 +34,7 @@
     String getName();
 
     @UserIdInt
-    int getUserId();
+    int getAppId();
 
     // flags that are associated with this uid, regardless of any package flags
     int getUidFlags();
@@ -65,4 +66,7 @@
     ArrayMap<String, ParsedProcess> getProcesses();
 
     boolean isPrivileged();
+
+    @NonNull
+    LegacyPermissionState getSharedUserLegacyPermissionState();
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6e4651c..c0abbf6 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -298,6 +298,7 @@
     // must match: config_doubleTapOnHomeBehavior in config.xml
     static final int DOUBLE_TAP_HOME_NOTHING = 0;
     static final int DOUBLE_TAP_HOME_RECENT_SYSTEM_UI = 1;
+    static final int DOUBLE_TAP_HOME_PIP_MENU = 2;
 
     static final int SHORT_PRESS_WINDOW_NOTHING = 0;
     static final int SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE = 1;
@@ -1743,11 +1744,15 @@
 
                 // Delay handling home if a double-tap is possible.
                 if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
-                    mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
-                    mHomeDoubleTapPending = true;
-                    mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
-                            ViewConfiguration.getDoubleTapTimeout());
-                    return -1;
+                    // For the picture-in-picture menu, only add the delay if a pip is there.
+                    if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_PIP_MENU
+                            || mPictureInPictureVisible) {
+                        mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
+                        mHomeDoubleTapPending = true;
+                        mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
+                                ViewConfiguration.getDoubleTapTimeout());
+                        return -1;
+                    }
                 }
 
                 // Post to main thread to avoid blocking input pipeline.
@@ -1780,7 +1785,7 @@
                 if (mHomeDoubleTapPending) {
                     mHomeDoubleTapPending = false;
                     mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
-                    handleDoubleTapOnHome();
+                    mHandler.post(this::handleDoubleTapOnHome);
                 // TODO(multi-display): Remove display id check once we support recents on
                 // multi-display
                 } else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
@@ -1798,13 +1803,29 @@
         }
 
         private void handleDoubleTapOnHome() {
-            if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
-                mHomeConsumed = true;
-                toggleRecentApps();
+            if (mHomeConsumed) {
+                return;
+            }
+            switch (mDoubleTapOnHomeBehavior) {
+                case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
+                    mHomeConsumed = true;
+                    toggleRecentApps();
+                    break;
+                case DOUBLE_TAP_HOME_PIP_MENU:
+                    mHomeConsumed = true;
+                    showPictureInPictureMenuInternal();
+                    break;
+                default:
+                    Log.w(TAG, "No action or undefined behavior for double tap home: "
+                            + mDoubleTapOnHomeBehavior);
+                    break;
             }
         }
 
         private void handleLongPressOnHome(int deviceId, long eventTime) {
+            if (mHomeConsumed) {
+                return;
+            }
             if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_NOTHING) {
                 return;
             }
@@ -2362,8 +2383,8 @@
         mDoubleTapOnHomeBehavior = res.getInteger(
                 com.android.internal.R.integer.config_doubleTapOnHomeBehavior);
         if (mDoubleTapOnHomeBehavior < DOUBLE_TAP_HOME_NOTHING ||
-                mDoubleTapOnHomeBehavior > DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
-            mDoubleTapOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
+                mDoubleTapOnHomeBehavior > DOUBLE_TAP_HOME_PIP_MENU) {
+            mDoubleTapOnHomeBehavior = DOUBLE_TAP_HOME_NOTHING;
         }
 
         mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_NOTHING;
@@ -5741,6 +5762,8 @@
                 return "DOUBLE_TAP_HOME_NOTHING";
             case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
                 return "DOUBLE_TAP_HOME_RECENT_SYSTEM_UI";
+            case DOUBLE_TAP_HOME_PIP_MENU:
+                return "DOUBLE_TAP_HOME_PIP_MENU";
             default:
                 return Integer.toString(behavior);
         }
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 040fffa8..19c8cd2 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -160,7 +160,7 @@
 
     private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal;
 
-    private EmergencyCallHelper mEmergencyCallHelper;
+    private CallStateHelper mCallStateHelper;
     private KeyguardManager mKeyguardManager;
 
     private int mCurrentUser = USER_NULL;
@@ -191,7 +191,7 @@
     public void onBootPhase(int phase) {
         if (phase == PHASE_SYSTEM_SERVICES_READY) {
             mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
-            mEmergencyCallHelper = new EmergencyCallHelper();
+            mCallStateHelper = new CallStateHelper();
         } else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
             mCameraPrivacyLightController = new CameraPrivacyLightController(mContext);
         }
@@ -702,7 +702,7 @@
         }
 
         private boolean canChangeIndividualSensorPrivacy(@UserIdInt int userId, int sensor) {
-            if (sensor == MICROPHONE && mEmergencyCallHelper.isInEmergencyCall()) {
+            if (sensor == MICROPHONE && mCallStateHelper.isInEmergencyCall()) {
                 // During emergency call the microphone toggle managed automatically
                 Log.i(TAG, "Can't change mic toggle during an emergency call");
                 return false;
@@ -1523,16 +1523,16 @@
         }
     }
 
-    private class EmergencyCallHelper {
+    private class CallStateHelper {
         private OutgoingEmergencyStateCallback mEmergencyStateCallback;
         private CallStateCallback mCallStateCallback;
 
         private boolean mIsInEmergencyCall;
         private boolean mMicUnmutedForEmergencyCall;
 
-        private Object mEmergencyStateLock = new Object();
+        private Object mCallStateLock = new Object();
 
-        EmergencyCallHelper() {
+        CallStateHelper() {
             mEmergencyStateCallback = new OutgoingEmergencyStateCallback();
             mCallStateCallback = new CallStateCallback();
 
@@ -1543,7 +1543,7 @@
         }
 
         boolean isInEmergencyCall() {
-            synchronized (mEmergencyStateLock) {
+            synchronized (mCallStateLock) {
                 return mIsInEmergencyCall;
             }
         }
@@ -1563,12 +1563,14 @@
             public void onCallStateChanged(int state) {
                 if (state == TelephonyManager.CALL_STATE_IDLE) {
                     onCallOver();
+                } else {
+                    onCall();
                 }
             }
         }
 
         private void onEmergencyCall() {
-            synchronized (mEmergencyStateLock) {
+            synchronized (mCallStateLock) {
                 if (!mIsInEmergencyCall) {
                     mIsInEmergencyCall = true;
                     if (mSensorPrivacyServiceImpl
@@ -1583,8 +1585,19 @@
             }
         }
 
+        private void onCall() {
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mCallStateLock) {
+                    mSensorPrivacyServiceImpl.showSensorUseDialog(MICROPHONE);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         private void onCallOver() {
-            synchronized (mEmergencyStateLock) {
+            synchronized (mCallStateLock) {
                 if (mIsInEmergencyCall) {
                     mIsInEmergencyCall = false;
                     if (mMicUnmutedForEmergencyCall) {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 6aafd4a..b4c54f9 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -69,6 +69,7 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.Xml;
+import android.view.Display;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -76,7 +77,9 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -143,6 +146,7 @@
     private final LockPatternUtils mLockPatternUtils;
     private final UserManager mUserManager;
     private final ActivityManager mActivityManager;
+    private VirtualDeviceManagerInternal mVirtualDeviceManager;
 
     @GuardedBy("mUserIsTrusted")
     private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray();
@@ -1293,9 +1297,57 @@
             mHandler.obtainMessage(MSG_UNREGISTER_LISTENER, trustListener).sendToTarget();
         }
 
+        /**
+         * @param uid: uid of the calling app (obtained via getCallingUid())
+         * @param displayId: the id of a Display
+         * @return Returns true if both of the following conditions hold -
+         * 1) the uid belongs to an app instead of a system core component; and
+         * 2) either the uid is running on a virtual device or the displayId
+         *    is owned by a virtual device
+         */
+        private boolean isAppOrDisplayOnAnyVirtualDevice(int uid, int displayId) {
+            if (UserHandle.isCore(uid)) {
+                return false;
+            }
+
+            if (mVirtualDeviceManager == null) {
+                mVirtualDeviceManager = LocalServices.getService(
+                        VirtualDeviceManagerInternal.class);
+                if (mVirtualDeviceManager == null) {
+                    // VirtualDeviceManager service may not have been published
+                    return false;
+                }
+            }
+
+            switch (displayId) {
+                case Display.INVALID_DISPLAY:
+                    // There is no Display object associated with the Context of the calling app.
+                    if (mVirtualDeviceManager.isAppRunningOnAnyVirtualDevice(uid)) {
+                        return true;
+                    }
+                    break;
+                case Display.DEFAULT_DISPLAY:
+                    // The DEFAULT_DISPLAY is by definition not virtual.
+                    break;
+                default:
+                    // Other display IDs can belong to logical displays created for other purposes.
+                    if (mVirtualDeviceManager.isDisplayOwnedByAnyVirtualDevice(displayId)) {
+                        return true;
+                    }
+                    break;
+            }
+            return false;
+        }
+
         @Override
-        public boolean isDeviceLocked(int userId) throws RemoteException {
-            userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
+        public boolean isDeviceLocked(int userId, int displayId) throws RemoteException {
+            int uid = getCallingUid();
+            if (isAppOrDisplayOnAnyVirtualDevice(uid, displayId)) {
+                // Virtual displays are considered insecure because they may be used for streaming
+                // to other devices.
+                return false;
+            }
+            userId = ActivityManager.handleIncomingUser(getCallingPid(), uid, userId,
                     false /* allowAll */, true /* requireFull */, "isDeviceLocked", null);
 
             final long token = Binder.clearCallingIdentity();
@@ -1310,8 +1362,15 @@
         }
 
         @Override
-        public boolean isDeviceSecure(int userId) throws RemoteException {
-            userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
+        public boolean isDeviceSecure(int userId, int displayId) throws RemoteException {
+            int uid = getCallingUid();
+            if (isAppOrDisplayOnAnyVirtualDevice(uid, displayId)) {
+                // Virtual displays are considered insecure because they may be used for streaming
+                // to other devices.
+                return false;
+            }
+
+            userId = ActivityManager.handleIncomingUser(getCallingPid(), uid, userId,
                     false /* allowAll */, true /* requireFull */, "isDeviceSecure", null);
 
             final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 580ab179..9f8d097 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1695,6 +1695,12 @@
     }
 
     @Override
+    public String getVoiceInteractorPackageName(IBinder callingVoiceInteractor) {
+        return LocalServices.getService(VoiceInteractionManagerInternal.class)
+                .getVoiceInteractorPackageName(callingVoiceInteractor);
+    }
+
+    @Override
     public int startAssistantActivity(String callingPackage, @NonNull String callingFeatureId,
             int callingPid, int callingUid, Intent intent, String resolvedType, Bundle bOptions,
             int userId) {
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index f61cc94..a83a033 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -65,7 +65,16 @@
         @Override
         public void binderDied() {
             synchronized (mGlobalLock) {
-                mOrganizersByFeatureIds.remove(mFeature).destroy();
+                IDisplayAreaOrganizer featureOrganizer = getOrganizerByFeature(mFeature);
+                if (featureOrganizer != null) {
+                    IBinder organizerBinder = featureOrganizer.asBinder();
+                    if (!organizerBinder.equals(mOrganizer.asBinder()) &&
+                               organizerBinder.isBinderAlive()) {
+                        Slog.d(TAG, "Dead organizer replaced for feature=" + mFeature);
+                        return;
+                    }
+                    mOrganizersByFeatureIds.remove(mFeature).destroy();
+                }
             }
         }
     }
@@ -172,7 +181,7 @@
                         organizer.asBinder(), uid);
                 mOrganizersByFeatureIds.entrySet().removeIf((entry) -> {
                     final boolean matches = entry.getValue().mOrganizer.asBinder()
-                            == organizer.asBinder();
+                            .equals(organizer.asBinder());
                     if (matches) {
                         entry.getValue().destroy();
                     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 818e4f6..e845034 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3127,12 +3127,16 @@
         mDisplayPolicy.switchUser();
     }
 
-    @Override
-    void removeIfPossible() {
-        if (isAnimating(TRANSITION | PARENTS)
+    private boolean shouldDeferRemoval() {
+        return isAnimating(TRANSITION | PARENTS)
                 // isAnimating is a legacy transition query and will be removed, so also add a
                 // check for whether this is in a shell-transition when not using legacy.
-                || mTransitionController.inTransition()) {
+                || mTransitionController.isTransitionOnDisplay(this);
+    }
+
+    @Override
+    void removeIfPossible() {
+        if (shouldDeferRemoval()) {
             mDeferredRemoval = true;
             return;
         }
@@ -3161,6 +3165,8 @@
             mInputMonitor.onDisplayRemoved();
             mWmService.mDisplayNotificationController.dispatchDisplayRemoved(this);
             mWmService.mAccessibilityController.onDisplayRemoved(mDisplayId);
+            mRootWindowContainer.mTaskSupervisor
+                    .getKeyguardController().onDisplayRemoved(mDisplayId);
         } finally {
             mDisplayReady = false;
         }
@@ -3174,7 +3180,8 @@
     /** Returns true if a removal action is still being deferred. */
     @Override
     boolean handleCompleteDeferredRemoval() {
-        final boolean stillDeferringRemoval = super.handleCompleteDeferredRemoval();
+        final boolean stillDeferringRemoval =
+                super.handleCompleteDeferredRemoval() || shouldDeferRemoval();
 
         if (!stillDeferringRemoval && mDeferredRemoval) {
             removeImmediately();
@@ -5915,8 +5922,6 @@
             forAllRootTasks(t -> {t.removeIfPossible("releaseSelfIfNeeded");});
         } else if (getTopRootTask() == null) {
             removeIfPossible();
-            mRootWindowContainer.mTaskSupervisor
-                    .getKeyguardController().onDisplayRemoved(mDisplayId);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 8b8fd2c..9f2188b 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -158,8 +158,8 @@
     /** The final animation targets derived from participants after promotion. */
     private ArrayList<WindowContainer> mTargets;
 
-    /** The main display running this transition. */
-    private DisplayContent mTargetDisplay;
+    /** The displays that this transition is running on. */
+    private final ArrayList<DisplayContent> mTargetDisplays = new ArrayList<>();
 
     /**
      * Set of participating windowtokens (activity/wallpaper) which are visible at the end of
@@ -209,6 +209,10 @@
         return mTransientLaunches != null && mTransientLaunches.contains(activity);
     }
 
+    boolean isOnDisplay(@NonNull DisplayContent dc) {
+        return mTargetDisplays.contains(dc);
+    }
+
     void setSeamlessRotation(@NonNull WindowContainer wc) {
         final ChangeInfo info = mChanges.get(wc);
         if (info == null) return;
@@ -280,6 +284,9 @@
             mChanges.put(wc, info);
         }
         mParticipants.add(wc);
+        if (wc.getDisplayContent() != null && !mTargetDisplays.contains(wc.getDisplayContent())) {
+            mTargetDisplays.add(wc.getDisplayContent());
+        }
         if (info.mShowWallpaper) {
             // Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
             final WindowState wallpaper =
@@ -516,15 +523,23 @@
             dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
         }
 
-        final AsyncRotationController asyncRotationController =
-                mTargetDisplay.getAsyncRotationController();
-        if (asyncRotationController != null) {
-            asyncRotationController.onTransitionFinished();
-        }
-        // Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
-        // so re-compute in case the IME target is changed after transition.
-        if (mTransientLaunches != null) {
-            mTargetDisplay.computeImeTarget(true /* updateImeTarget */);
+        for (int i = 0; i < mTargetDisplays.size(); ++i) {
+            final DisplayContent dc = mTargetDisplays.get(i);
+            final AsyncRotationController asyncRotationController = dc.getAsyncRotationController();
+            if (asyncRotationController != null) {
+                asyncRotationController.onTransitionFinished();
+            }
+            if (mTransientLaunches != null) {
+                // Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
+                // so re-compute in case the IME target is changed after transition.
+                for (int t = 0; t < mTransientLaunches.size(); ++t) {
+                    if (mTransientLaunches.valueAt(t).getDisplayContent() == dc) {
+                        dc.computeImeTarget(true /* updateImeTarget */);
+                        break;
+                    }
+                }
+            }
+            dc.handleCompleteDeferredRemoval();
         }
     }
 
@@ -555,19 +570,13 @@
             Slog.e(TAG, "Unexpected Sync ID " + syncId + ". Expected " + mSyncId);
             return;
         }
-        boolean hasWallpaper = false;
-        DisplayContent dc = null;
-        for (int i = mParticipants.size() - 1; i >= 0; --i) {
-            final WindowContainer<?> wc = mParticipants.valueAt(i);
-            if (dc == null && wc.mDisplayContent != null) {
-                dc = wc.mDisplayContent;
-            }
-            if (!hasWallpaper && isWallpaper(wc)) {
-                hasWallpaper = true;
-            }
+        if (mTargetDisplays.isEmpty()) {
+            mTargetDisplays.add(mController.mAtm.mRootWindowContainer.getDefaultDisplay());
         }
-        if (dc == null) dc = mController.mAtm.mRootWindowContainer.getDefaultDisplay();
-        mTargetDisplay = dc;
+        // While there can be multiple DC's involved. For now, we just use the first one as
+        // the "primary" one for most things. Eventually, this will need to change, but, for the
+        // time being, we don't have full cross-display transitions so it isn't a problem.
+        final DisplayContent dc = mTargetDisplays.get(0);
 
         if (mState == STATE_ABORT) {
             mController.abort(this);
@@ -577,8 +586,11 @@
             return;
         }
         // Ensure that wallpaper visibility is updated with the latest wallpaper target.
-        if (hasWallpaper) {
-            dc.mWallpaperController.adjustWallpaperWindows();
+        for (int i = mParticipants.size() - 1; i >= 0; --i) {
+            final WindowContainer<?> wc = mParticipants.valueAt(i);
+            if (isWallpaper(wc) && wc.getDisplayContent() != null) {
+                wc.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
+            }
         }
 
         mState = STATE_PLAYING;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 3d9d824..c267cba 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -256,6 +256,17 @@
         return false;
     }
 
+    /** @return {@code true} if wc is in a participant subtree */
+    boolean isTransitionOnDisplay(@NonNull DisplayContent dc) {
+        if (mCollectingTransition != null && mCollectingTransition.isOnDisplay(dc)) {
+            return true;
+        }
+        for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+            if (mPlayingTransitions.get(i).isOnDisplay(dc)) return true;
+        }
+        return false;
+    }
+
     /**
      * @return {@code true} if {@param ar} is part of a transient-launch activity in an active
      * transition.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8536b08..4d1bc22 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1364,6 +1364,7 @@
         float lightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
         float ambientShadowAlpha = a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0);
         float spotShadowAlpha = a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0);
+        a.recycle();
         float[] ambientColor = {0.f, 0.f, 0.f, ambientShadowAlpha};
         float[] spotColor = {0.f, 0.f, 0.f, spotShadowAlpha};
         SurfaceControl.setGlobalShadowSettings(ambientColor, spotColor, lightY, lightZ,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f8ace0c..091d879 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7331,7 +7331,7 @@
     }
 
     @Override
-    public void reportPasswordChanged(@UserIdInt int userId) {
+    public void reportPasswordChanged(PasswordMetrics metrics, @UserIdInt int userId) {
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
@@ -7363,6 +7363,10 @@
             affectedUserIds.addAll(removeCaApprovalsIfNeeded(userId));
             saveSettingsForUsersLocked(affectedUserIds);
         }
+        if (mInjector.securityLogIsLoggingEnabled()) {
+            SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_CHANGED,
+                    /* complexity */ metrics.determineComplexity(), /*user*/ userId);
+        }
     }
 
     /**
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 717168f..f72b23c 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -43,6 +43,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.ParcelUuid;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -64,6 +65,14 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.UUID;
+
+// NOTE about locking order:
+// if there is a path that syncs on BOTH mDevicesByInfo AND mDeviceConnections,
+// this order must be observed
+//   1. synchronized (mDevicesByInfo)
+//   2. synchronized (mDeviceConnections)
+//TODO Introduce a single lock object to lock the whole state and avoid the requirement above.
 
 public class MidiService extends IMidiManager.Stub {
 
@@ -122,6 +131,9 @@
     // UID of BluetoothMidiService
     private int mBluetoothServiceUid;
 
+    private static final UUID MIDI_SERVICE = UUID.fromString(
+            "03B80E5A-EDE8-4B33-A751-6CE34EC4C700");
+
     // PackageMonitor for listening to package changes
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         @Override
@@ -434,14 +446,19 @@
                         public void onServiceConnected(ComponentName name, IBinder service) {
                             IMidiDeviceServer server = null;
                             if (mBluetoothDevice != null) {
-                                IBluetoothMidiService mBluetoothMidiService = IBluetoothMidiService.Stub.asInterface(service);
-                                try {
-                                    // We need to explicitly add the device in a separate method
-                                    // because onBind() is only called once.
-                                    IBinder deviceBinder = mBluetoothMidiService.addBluetoothDevice(mBluetoothDevice);
-                                    server = IMidiDeviceServer.Stub.asInterface(deviceBinder);
-                                } catch(RemoteException e) {
-                                    Log.e(TAG, "Could not call addBluetoothDevice()", e);
+                                IBluetoothMidiService mBluetoothMidiService =
+                                        IBluetoothMidiService.Stub.asInterface(service);
+                                if (mBluetoothMidiService != null) {
+                                    try {
+                                        // We need to explicitly add the device in a separate method
+                                        // because onBind() is only called once.
+                                        IBinder deviceBinder =
+                                                mBluetoothMidiService.addBluetoothDevice(
+                                                        mBluetoothDevice);
+                                        server = IMidiDeviceServer.Stub.asInterface(deviceBinder);
+                                    } catch (RemoteException e) {
+                                        Log.e(TAG, "Could not call addBluetoothDevice()", e);
+                                    }
                                 }
                             } else {
                                 server = IMidiDeviceServer.Stub.asInterface(service);
@@ -482,19 +499,19 @@
         }
 
         public void removeDeviceConnection(DeviceConnection connection) {
-            synchronized (mDeviceConnections) {
-                mDeviceConnections.remove(connection);
+            synchronized (mDevicesByInfo) {
+                synchronized (mDeviceConnections) {
+                    mDeviceConnections.remove(connection);
 
-                if (mDeviceConnections.size() == 0 && mServiceConnection != null) {
-                    mContext.unbindService(mServiceConnection);
-                    mServiceConnection = null;
-                    if (mBluetoothDevice != null) {
-                        // Bluetooth devices are ephemeral - remove when no clients exist
-                        synchronized (mDevicesByInfo) {
+                    if (mDeviceConnections.size() == 0 && mServiceConnection != null) {
+                        mContext.unbindService(mServiceConnection);
+                        mServiceConnection = null;
+                        if (mBluetoothDevice != null) {
+                            // Bluetooth devices are ephemeral - remove when no clients exist
                             closeLocked();
+                        } else {
+                            setDeviceServer(null);
                         }
-                    } else {
-                        setDeviceServer(null);
                     }
                 }
             }
@@ -589,6 +606,20 @@
         }
     }
 
+    // Note, this isn't useful at connect-time because the service UUIDs haven't
+    // been gathered yet.
+    private boolean isBLEMIDIDevice(BluetoothDevice btDevice) {
+        ParcelUuid[] uuids = btDevice.getUuids();
+        if (uuids != null) {
+            for (ParcelUuid uuid : uuids) {
+                if (uuid.getUuid().equals(MIDI_SERVICE)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private final BroadcastReceiver mBleMidiReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -603,6 +634,8 @@
                     Log.d(TAG, "ACTION_ACL_CONNECTED");
                     BluetoothDevice btDevice =
                             intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                    // We can't determine here if this is a BLD-MIDI device, so go ahead and try
+                    // to open as a MIDI device, further down it will get figured out.
                     openBluetoothDevice(btDevice);
                 }
                 break;
@@ -611,7 +644,11 @@
                     Log.d(TAG, "ACTION_ACL_DISCONNECTED");
                     BluetoothDevice btDevice =
                             intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                    closeBluetoothDevice(btDevice);
+                    // We DO know at this point if we are disconnecting a MIDI device, so
+                    // don't bother if we are not.
+                    if (isBLEMIDIDevice(btDevice)) {
+                        closeBluetoothDevice(btDevice);
+                    }
                 }
                 break;
             }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index 816dbdb..936940f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -17,6 +17,8 @@
 package com.android.server.am;
 
 import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
 import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.app.ActivityManager.RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
@@ -52,7 +54,12 @@
 import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND;
 import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND_SERVICE;
 import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATT_DIMENS;
+import static com.android.server.am.AppPermissionTracker.AppPermissionPolicy;
 import static com.android.server.am.AppRestrictionController.STOCK_PM_FLAGS;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_FGS_LOCATION;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_FGS_MEDIA_PLAYBACK;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_MEDIA_SESSION;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -103,6 +110,7 @@
 import android.os.SystemClock;
 import android.os.UidBatteryConsumer;
 import android.os.UserHandle;
+import android.permission.PermissionManager;
 import android.provider.DeviceConfig;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -127,6 +135,7 @@
 import com.android.server.am.AppRestrictionController.UidBatteryUsageProvider;
 import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
 import com.android.server.apphibernation.AppHibernationManagerInternal;
+import com.android.server.notification.NotificationManagerInternal;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.usage.AppStandbyInternal;
@@ -151,6 +160,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
 
 /**
  * Tests for {@link AppRestrictionController}.
@@ -215,6 +225,8 @@
     @Mock private PackageManager mPackageManager;
     @Mock private PackageManagerInternal mPackageManagerInternal;
     @Mock private NotificationManager mNotificationManager;
+    @Mock private NotificationManagerInternal mNotificationManagerInternal;
+    @Mock private PermissionManager mPermissionManager;
     @Mock private PermissionManagerServiceInternal mPermissionManagerServiceInternal;
     @Mock private MediaSessionManager mMediaSessionManager;
     @Mock private RoleManager mRoleManager;
@@ -250,6 +262,7 @@
     private AppBindServiceEventsTracker mAppBindServiceEventsTracker;
     private AppFGSTracker mAppFGSTracker;
     private AppMediaSessionTracker mAppMediaSessionTracker;
+    private AppPermissionTracker mAppPermissionTracker;
 
     @Before
     public void setUp() throws Exception {
@@ -289,12 +302,16 @@
                 doReturn(AppOpsManager.MODE_IGNORED)
                         .when(mAppOpsManager)
                         .checkOpNoThrow(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, uid, packageName);
-                doReturn(PERMISSION_DENIED)
-                        .when(mPermissionManagerServiceInternal)
-                        .checkUidPermission(uid, ACCESS_BACKGROUND_LOCATION);
-                doReturn(PERMISSION_DENIED)
-                        .when(mPermissionManagerServiceInternal)
-                        .checkPermission(packageName, ACCESS_BACKGROUND_LOCATION, userId);
+                final String[] permissions = new String[] {ACCESS_BACKGROUND_LOCATION,
+                        ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION};
+                for (String permission : permissions) {
+                    doReturn(PERMISSION_DENIED)
+                            .when(mPermissionManagerServiceInternal)
+                            .checkUidPermission(uid, permission);
+                    doReturn(PERMISSION_DENIED)
+                            .when(mPermissionManagerServiceInternal)
+                            .checkPermission(packageName, permission, userId);
+                }
             }
             doReturn(appStandbyInfoList).when(mAppStandbyInternal).getAppStandbyBuckets(userId);
         }
@@ -536,11 +553,14 @@
         final float bgRestrictedThreshold = 4.0f;
         final float bgRestrictedThresholdMah =
                 BATTERY_FULL_CHARGE_MAH * bgRestrictedThreshold / 100.0f;
+        final int testPid = 1234;
+        final int notificationId = 1000;
 
         DeviceConfigSession<Boolean> bgCurrentDrainMonitor = null;
         DeviceConfigSession<Long> bgCurrentDrainWindow = null;
         DeviceConfigSession<Float> bgCurrentDrainRestrictedBucketThreshold = null;
         DeviceConfigSession<Float> bgCurrentDrainBgRestrictedThreshold = null;
+        DeviceConfigSession<Boolean> bgPromptFgsWithNotiToBgRestricted = null;
 
         mBgRestrictionController.addAppBackgroundRestrictionListener(listener);
 
@@ -587,10 +607,24 @@
                             isLowRamDeviceStatic() ? 1 : 0]);
             bgCurrentDrainBgRestrictedThreshold.set(bgRestrictedThreshold);
 
+            bgPromptFgsWithNotiToBgRestricted = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    ConstantsObserver.KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED,
+                    DeviceConfig::getBoolean,
+                    mContext.getResources().getBoolean(
+                            R.bool.config_bg_prompt_fgs_with_noti_to_bg_restricted));
+            bgPromptFgsWithNotiToBgRestricted.set(true);
+
             mCurrentTimeMillis = 10_000L;
             doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
             doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
             doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(anyObject());
+            doReturn(true).when(mNotificationManagerInternal).isNotificationShown(
+                    testPkgName, null, notificationId, testUser);
+            mAppFGSTracker.onForegroundServiceStateChanged(testPkgName, testUid,
+                    testPid, true);
+            mAppFGSTracker.onForegroundServiceNotificationUpdated(
+                    testPkgName, testUid, notificationId);
 
             runTestBgCurrentDrainMonitorOnce(listener, stats, uids,
                     new double[]{restrictBucketThresholdMah - 1, 0},
@@ -721,6 +755,85 @@
             Thread.sleep(windowMs);
             clearInvocations(mInjector.getAppStandbyInternal());
             clearInvocations(mBgRestrictionController);
+
+            // We're not going to prompt the user if the abusive app has a FGS with notification.
+            bgPromptFgsWithNotiToBgRestricted.set(false);
+
+            runTestBgCurrentDrainMonitorOnce(listener, stats, uids,
+                    new double[]{bgRestrictedThresholdMah + 1, 0},
+                    new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+                    () -> {
+                        doReturn(mCurrentTimeMillis).when(stats).getStatsStartTimestamp();
+                        doReturn(mCurrentTimeMillis + windowMs)
+                                .when(stats).getStatsEndTimestamp();
+                        mCurrentTimeMillis += windowMs + 1;
+                        // We won't change restriction level automatically because it needs
+                        // user consent.
+                        try {
+                            listener.verify(timeout, testUid, testPkgName,
+                                    RESTRICTION_LEVEL_BACKGROUND_RESTRICTED);
+                            fail("There shouldn't be level change event like this");
+                        } catch (Exception e) {
+                            // Expected.
+                        }
+                        verify(mInjector.getAppStandbyInternal(), never()).setAppStandbyBucket(
+                                eq(testPkgName),
+                                eq(STANDBY_BUCKET_RARE),
+                                eq(testUser),
+                                anyInt(), anyInt());
+                        // We should have requested to goto background restricted level.
+                        verify(mBgRestrictionController, times(1)).handleRequestBgRestricted(
+                                eq(testPkgName),
+                                eq(testUid));
+                        // However, we won't have the prompt to user posted because the policy
+                        // is not to show that for FGS with notification.
+                        checkNotificationShown(new String[] {testPkgName}, never(), false);
+                    });
+
+            // Pretend we have the notification dismissed.
+            mAppFGSTracker.onForegroundServiceNotificationUpdated(
+                    testPkgName, testUid, -notificationId);
+            clearInvocations(mInjector.getAppStandbyInternal());
+            clearInvocations(mBgRestrictionController);
+
+            runTestBgCurrentDrainMonitorOnce(listener, stats, uids,
+                    new double[]{bgRestrictedThresholdMah + 1, 0},
+                    new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+                    () -> {
+                        doReturn(mCurrentTimeMillis).when(stats).getStatsStartTimestamp();
+                        doReturn(mCurrentTimeMillis + windowMs)
+                                .when(stats).getStatsEndTimestamp();
+                        mCurrentTimeMillis += windowMs + 1;
+                        // We won't change restriction level automatically because it needs
+                        // user consent.
+                        try {
+                            listener.verify(timeout, testUid, testPkgName,
+                                    RESTRICTION_LEVEL_BACKGROUND_RESTRICTED);
+                            fail("There shouldn't be level change event like this");
+                        } catch (Exception e) {
+                            // Expected.
+                        }
+                        verify(mInjector.getAppStandbyInternal(), never()).setAppStandbyBucket(
+                                eq(testPkgName),
+                                eq(STANDBY_BUCKET_RARE),
+                                eq(testUser),
+                                anyInt(), anyInt());
+                        // We should have requested to goto background restricted level.
+                        verify(mBgRestrictionController, times(1)).handleRequestBgRestricted(
+                                eq(testPkgName),
+                                eq(testUid));
+                        // Verify we have the notification posted now because its FGS is invisible.
+                        checkNotificationShown(new String[] {testPkgName}, atLeast(1), true);
+                    });
+
+            // Pretend notification is back on.
+            mAppFGSTracker.onForegroundServiceNotificationUpdated(
+                    testPkgName, testUid, notificationId);
+            // Now we'll prompt the user even it has a FGS with notification.
+            bgPromptFgsWithNotiToBgRestricted.set(true);
+            clearInvocations(mInjector.getAppStandbyInternal());
+            clearInvocations(mBgRestrictionController);
+
             runTestBgCurrentDrainMonitorOnce(listener, stats, uids,
                     new double[]{bgRestrictedThresholdMah + 1, 0},
                     new double[]{0, restrictBucketThresholdMah - 1}, zeros,
@@ -785,6 +898,7 @@
             closeIfNotNull(bgCurrentDrainWindow);
             closeIfNotNull(bgCurrentDrainRestrictedBucketThreshold);
             closeIfNotNull(bgCurrentDrainBgRestrictedThreshold);
+            closeIfNotNull(bgPromptFgsWithNotiToBgRestricted);
         }
     }
 
@@ -1179,7 +1293,8 @@
                     .checkPermission(packageName, perm, UserHandle.getUserId(uid));
             doReturn(PERMISSION_GRANTED)
                     .when(mPermissionManagerServiceInternal)
-                    .checkUidPermission(uid, ACCESS_BACKGROUND_LOCATION);
+                    .checkUidPermission(uid, perm);
+            mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
         }
 
         if (mediaControllers != null) {
@@ -1204,7 +1319,8 @@
                     .checkPermission(packageName, perm, UserHandle.getUserId(uid));
             doReturn(PERMISSION_DENIED)
                     .when(mPermissionManagerServiceInternal)
-                    .checkUidPermission(uid, ACCESS_BACKGROUND_LOCATION);
+                    .checkUidPermission(uid, perm);
+            mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
         }
         if (topStateThread != null) {
             topStateThread.join();
@@ -1272,6 +1388,10 @@
         DeviceConfigSession<Long> bgLocationMinDurationThreshold = null;
         DeviceConfigSession<Boolean> bgCurrentDrainEventDurationBasedThresholdEnabled = null;
         DeviceConfigSession<Boolean> bgBatteryExemptionEnabled = null;
+        DeviceConfigSession<Integer> bgBatteryExemptionTypes = null;
+        DeviceConfigSession<Boolean> bgPermissionMonitorEnabled = null;
+        DeviceConfigSession<String> bgPermissionsInMonitor = null;
+        DeviceConfigSession<Boolean> bgCurrentDrainHighThresholdByBgLocation = null;
 
         mBgRestrictionController.addAppBackgroundRestrictionListener(listener);
 
@@ -1370,6 +1490,38 @@
                     AppBatteryExemptionPolicy.DEFAULT_BG_BATTERY_EXEMPTION_ENABLED);
             bgBatteryExemptionEnabled.set(false);
 
+            bgBatteryExemptionTypes = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES,
+                    DeviceConfig::getInt,
+                    mContext.getResources().getInteger(
+                            R.integer.config_bg_current_drain_exempted_types));
+            bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_FGS_MEDIA_PLAYBACK
+                    | STATE_TYPE_FGS_LOCATION | STATE_TYPE_PERMISSION);
+
+            bgPermissionMonitorEnabled = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    AppPermissionPolicy.KEY_BG_PERMISSION_MONITOR_ENABLED,
+                    DeviceConfig::getBoolean,
+                    AppPermissionPolicy.DEFAULT_BG_PERMISSION_MONITOR_ENABLED);
+            bgPermissionMonitorEnabled.set(true);
+
+            bgPermissionsInMonitor = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    AppPermissionPolicy.KEY_BG_PERMISSION_MONITOR_ENABLED,
+                    DeviceConfig::getString,
+                    Arrays.stream(AppPermissionPolicy.DEFAULT_BG_PERMISSIONS_IN_MONITOR)
+                    .collect(Collectors.joining(",")));
+            bgPermissionsInMonitor.set(ACCESS_FINE_LOCATION);
+
+            bgCurrentDrainHighThresholdByBgLocation = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION,
+                    DeviceConfig::getBoolean,
+                    mContext.getResources().getBoolean(
+                            R.bool.config_bg_current_drain_high_threshold_by_bg_location));
+            bgCurrentDrainHighThresholdByBgLocation.set(true);
+
             mCurrentTimeMillis = 10_000L;
             doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
             doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
@@ -1519,15 +1671,44 @@
             setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
             mAppBatteryPolicy.reset();
 
-            // Run with bg location permission, with higher current drain.
+            // Turn off the higher threshold for bg location access.
+            bgCurrentDrainHighThresholdByBgLocation.set(false);
+
+            // Run with bg location permission, with moderate current drain.
             runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
                     FOREGROUND_SERVICE_TYPE_NONE, 0, false,
                     ACCESS_BACKGROUND_LOCATION, null, null, listener, stats, uids,
-                    new double[]{restrictBucketHighThresholdMah - 1, 0},
+                    new double[]{restrictBucketThresholdMah - 1, 0},
                     new double[]{0, restrictBucketThresholdMah - 1}, zeros,
                     true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
                     null, windowMs, null, null, null);
 
+            // Run with bg location permission, with a bit higher current drain.
+            runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+                    FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+                    ACCESS_BACKGROUND_LOCATION, null, null, listener, stats, uids,
+                    new double[]{restrictBucketThresholdMah + 1, 0},
+                    new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+                    false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
+                    null, windowMs, null, null, null);
+
+            // Start over.
+            resetBgRestrictionController();
+            setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
+            mAppBatteryPolicy.reset();
+
+            // Turn on the higher threshold for bg location access.
+            bgCurrentDrainHighThresholdByBgLocation.set(true);
+
+            // Run with bg location permission, with higher current drain.
+            runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+                    FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+                    ACCESS_BACKGROUND_LOCATION , null, null, listener, stats, uids,
+                    new double[]{restrictBucketHighThresholdMah - 1, 0},
+                    new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+                    true , RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
+                    null, windowMs, null,  null, null);
+
             // Run with bg location permission, with even higher current drain.
             runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
                     FOREGROUND_SERVICE_TYPE_NONE, 0, false,
@@ -1610,6 +1791,36 @@
                     true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
                     null, windowMs, initialBg, initialFgs, initialFg);
 
+            // Set the policy to exempt media session and permission.
+            bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_PERMISSION);
+            // Start over.
+            resetBgRestrictionController();
+            setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
+            mAppBatteryPolicy.reset();
+
+            // Run with coarse location permission, with high current drain.
+            runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+                    FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+                    ACCESS_COARSE_LOCATION, null, null, listener, stats, uids,
+                    new double[]{restrictBucketThresholdMah + 1, 0},
+                    new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+                    false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
+                    null, windowMs, initialBg, initialFgs, initialFg);
+
+            // Start over.
+            resetBgRestrictionController();
+            setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
+            mAppBatteryPolicy.reset();
+
+            // Run with fine location permission, with high current drain.
+            runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+                    FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+                    ACCESS_FINE_LOCATION, null, null, listener, stats, uids,
+                    new double[]{restrictBucketThresholdMah + 1, 0},
+                    new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+                    true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
+                    null, windowMs, initialBg, initialFgs, initialFg);
+
             // Start over.
             resetBgRestrictionController();
             setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
@@ -1639,6 +1850,10 @@
                     true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
                     null, windowMs, initialBg, initialFgs, initialFg);
 
+            // Set the policy to exempt all.
+            bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_FGS_MEDIA_PLAYBACK
+                    | STATE_TYPE_FGS_LOCATION | STATE_TYPE_PERMISSION);
+
             // Start over.
             resetBgRestrictionController();
             setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
@@ -1673,6 +1888,10 @@
             closeIfNotNull(bgLocationMinDurationThreshold);
             closeIfNotNull(bgCurrentDrainEventDurationBasedThresholdEnabled);
             closeIfNotNull(bgBatteryExemptionEnabled);
+            closeIfNotNull(bgBatteryExemptionTypes);
+            closeIfNotNull(bgPermissionMonitorEnabled);
+            closeIfNotNull(bgPermissionsInMonitor);
+            closeIfNotNull(bgCurrentDrainHighThresholdByBgLocation);
         }
     }
 
@@ -1693,6 +1912,15 @@
             mAppBatteryExemptionTracker.reset();
             mAppBatteryPolicy.reset();
         }
+        if (perm != null) {
+            doReturn(PERMISSION_GRANTED)
+                    .when(mPermissionManagerServiceInternal)
+                    .checkPermission(packageName, perm, UserHandle.getUserId(uid));
+            doReturn(PERMISSION_GRANTED)
+                    .when(mPermissionManagerServiceInternal)
+                    .checkUidPermission(uid, perm);
+            mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
+        }
         runExemptionTestOnce(
                 packageName, uid, pid, serviceType, sleepMs, stopAfterSleep,
                 perm, mediaControllers, topStateChanges, resetFGSTracker, false,
@@ -1742,6 +1970,15 @@
                     );
                 }
         );
+        if (perm != null) {
+            doReturn(PERMISSION_DENIED)
+                    .when(mPermissionManagerServiceInternal)
+                    .checkPermission(packageName, perm, UserHandle.getUserId(uid));
+            doReturn(PERMISSION_DENIED)
+                    .when(mPermissionManagerServiceInternal)
+                    .checkUidPermission(uid, perm);
+            mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
+        }
     }
 
     @Test
@@ -2307,6 +2544,11 @@
                                 BackgroundRestrictionTest.class),
                         BackgroundRestrictionTest.this);
                 controller.addAppStateTracker(mAppBindServiceEventsTracker);
+                mAppPermissionTracker = new AppPermissionTracker(mContext, controller,
+                        TestAppPermissionTrackerInjector.class.getDeclaredConstructor(
+                                BackgroundRestrictionTest.class),
+                        BackgroundRestrictionTest.this);
+                controller.addAppStateTracker(mAppPermissionTracker);
             } catch (NoSuchMethodException e) {
                 // Won't happen.
             }
@@ -2401,6 +2643,11 @@
         AppBatteryExemptionTracker getAppBatteryExemptionTracker() {
             return mAppBatteryExemptionTracker;
         }
+
+        @Override
+        AppPermissionTracker getAppPermissionTracker() {
+            return mAppPermissionTracker;
+        }
     }
 
     private class TestBaseTrackerInjector<T extends BaseAppStatePolicy>
@@ -2461,6 +2708,19 @@
         }
 
         @Override
+        NotificationManagerInternal getNotificationManagerInternal() {
+            return BackgroundRestrictionTest.this.mNotificationManagerInternal;
+        }
+
+        PackageManagerInternal getPackageManagerInternal() {
+            return BackgroundRestrictionTest.this.mPackageManagerInternal;
+        }
+
+        PermissionManager getPermissionManager() {
+            return BackgroundRestrictionTest.this.mPermissionManager;
+        }
+
+        @Override
         long getServiceStartForegroundTimeout() {
             return 1_000; // ms
         }
@@ -2490,6 +2750,10 @@
             extends TestBaseTrackerInjector<AppMediaSessionPolicy> {
     }
 
+    private class TestAppPermissionTrackerInjector
+            extends TestBaseTrackerInjector<AppPermissionPolicy> {
+    }
+
     private class TestAppBroadcastEventsTrackerInjector
             extends TestBaseTrackerInjector<AppBroadcastEventsPolicy> {
         @Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 7d42a52..0319d8d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -43,6 +43,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.when;
 
 import android.app.job.JobInfo;
@@ -137,7 +138,7 @@
                 .setOverrideDeadline(12)
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
                 .build();
-        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+        when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
         assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false);
     }
 
@@ -146,7 +147,7 @@
         final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
                 .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
                 .build();
-        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+        when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
         assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false);
     }
 
@@ -155,7 +156,7 @@
         final JobInfo networkJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
                 .build();
-        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+        when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
         assertEffectiveBucketForMediaExemption(createJobStatus(networkJob), false);
     }
 
@@ -165,7 +166,8 @@
                 .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
                 .build();
-        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn("not.test.package");
+        when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0)))
+                .thenReturn("not.test.package");
         assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), false);
     }
 
@@ -178,13 +180,25 @@
                 .addTriggerContentUri(new JobInfo.TriggerContentUri(nonEligibleUri, 0))
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
                 .build();
-        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+        when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
         assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), false);
     }
 
     @Test
+    public void testMediaBackupExemption_lowPriorityJobs() {
+        when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
+        final JobInfo.Builder jobBuilder = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+                .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+        assertEffectiveBucketForMediaExemption(
+                createJobStatus(jobBuilder.setPriority(JobInfo.PRIORITY_LOW).build()), false);
+        assertEffectiveBucketForMediaExemption(
+                createJobStatus(jobBuilder.setPriority(JobInfo.PRIORITY_MIN).build()), false);
+    }
+
+    @Test
     public void testMediaBackupExemptionGranted() {
-        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+        when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
         final JobInfo imageUriJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
                 .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 40bd800..a0ac506 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1794,6 +1794,98 @@
         }
     }
 
+    /**
+     * Test getTimeUntilQuotaConsumedLocked when allowed time equals the bucket window size.
+     */
+    @Test
+    public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (24 * HOUR_IN_MILLIS),
+                        mQcConstants.MAX_EXECUTION_TIME_MS - 10 * MINUTE_IN_MILLIS, 5),
+                false);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
+                false);
+
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+                10 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 10 * MINUTE_IN_MILLIS);
+        // window size = allowed time, so jobs can essentially run non-stop until they reach the
+        // max execution time.
+        setStandbyBucket(EXEMPTED_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(0,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 10 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+        }
+    }
+
+    /**
+     * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
+     * window and the session is rolling out of the window.
+     */
+    @Test
+    public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (24 * HOUR_IN_MILLIS),
+                        10 * MINUTE_IN_MILLIS, 5), false);
+        setStandbyBucket(RARE_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(0,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(10 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+        }
+
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (8 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+        setStandbyBucket(FREQUENT_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(0,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(10 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+        }
+
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (2 * HOUR_IN_MILLIS),
+                        10 * MINUTE_IN_MILLIS, 5), false);
+        setStandbyBucket(WORKING_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(0,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(10 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+        }
+
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
+                false);
+        // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
+        // max execution time.
+        setStandbyBucket(ACTIVE_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(0,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+        }
+    }
+
     @Test
     public void testIsWithinQuotaLocked_NeverApp() {
         synchronized (mQuotaController.mLock) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
index b89f36f..f35986b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -230,7 +230,7 @@
         val pair = createBasicAndroidPackage(STATIC_LIB_PACKAGE_NAME + "_" + 10, 10L,
             staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = 10L)
         val parsedPackage = pair.second as ParsedPackage
-        val scanRequest = ScanRequest(parsedPackage, null, null, null,
+        val scanRequest = ScanRequest(parsedPackage, null, null, null, null,
             null, null, null, 0, 0, false, null, null)
         val scanResult = ScanResult(scanRequest, true, null, null, false, 0, null, null, null)
 
@@ -329,7 +329,7 @@
         val packageSetting = mRule.system()
             .createBasicSettingBuilder(pair.first.parentFile, parsedPackage.hideAsFinal())
             .setPkgFlags(ApplicationInfo.FLAG_SYSTEM).build()
-        val scanRequest = ScanRequest(parsedPackage, null, null, null,
+        val scanRequest = ScanRequest(parsedPackage, null, null, null, null,
             null, null, null, 0, 0, false, null, null)
         val scanResult = ScanResult(scanRequest, true, packageSetting, null,
             false, 0, null, null, listOf(testInfo))
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index 6a7d298..68994e6 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -1,6 +1,6 @@
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
-per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
+per-file *Bluetooth* = file:platform/packages/modules/Bluetooth:master:/framework/java/android/bluetooth/OWNERS
 per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
 per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
 per-file BatteryServiceTest.java = file:platform/hardware/interfaces:/health/aidl/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index e9b5b62..18e0f29 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -16,8 +16,23 @@
 
 package com.android.server.am;
 
-import static org.junit.Assert.assertNull;
+import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
+import static android.content.Intent.ACTION_BOOT_COMPLETED;
+import static android.content.Intent.ACTION_LOCKED_BOOT_COMPLETED;
+import static android.content.Intent.ACTION_TIME_CHANGED;
 
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_ALL;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
+import static com.android.server.am.BroadcastDispatcher.DeferredBootCompletedBroadcastPerUser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+
+import android.app.ActivityManagerInternal;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -25,10 +40,14 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
 
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -44,6 +63,24 @@
 @Presubmit
 public class BroadcastRecordTest {
 
+    private static final int USER0 = UserHandle.USER_SYSTEM;
+    private static final int USER1 = USER0 + 1;
+    private static final int[] USER_LIST = new int[] {USER0, USER1};
+    private static final String PACKAGE1 = "pkg1";
+    private static final String PACKAGE2 = "pkg2";
+    private static final String PACKAGE3 = "pkg3";
+    private static final String PACKAGE4 = "pkg4";
+    private static final String[] PACKAGE_LIST = new String[] {PACKAGE1, PACKAGE2, PACKAGE3,
+            PACKAGE4};
+
+    @Mock
+    ActivityManagerInternal mActivityManagerInternal;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
     @Test
     public void testCleanupDisabledPackageReceivers() {
         final int user0 = UserHandle.USER_SYSTEM;
@@ -61,39 +98,232 @@
 
         // With given package:
         // Send to all users, cleanup a package of all users.
-        final BroadcastRecord recordAllAll = createBroadcastRecord(receiversM, UserHandle.USER_ALL);
+        final BroadcastRecord recordAllAll = createBroadcastRecord(receiversM, UserHandle.USER_ALL,
+                new Intent());
         cleanupDisabledPackageReceivers(recordAllAll, pkgToCleanup, UserHandle.USER_ALL);
         assertNull(verifyRemaining(recordAllAll, excludeReceivers(receiversM, pkgToCleanup, -1)));
 
         // Send to all users, cleanup a package of one user.
-        final BroadcastRecord recordAllOne = createBroadcastRecord(receiversM, UserHandle.USER_ALL);
+        final BroadcastRecord recordAllOne = createBroadcastRecord(receiversM, UserHandle.USER_ALL,
+                new Intent());
         cleanupDisabledPackageReceivers(recordAllOne, pkgToCleanup, user0);
         assertNull(verifyRemaining(recordAllOne,
                 excludeReceivers(receiversM, pkgToCleanup, user0)));
 
         // Send to one user, cleanup a package of all users.
-        final BroadcastRecord recordOneAll = createBroadcastRecord(receiversU0, user0);
+        final BroadcastRecord recordOneAll = createBroadcastRecord(receiversU0, user0,
+                new Intent());
         cleanupDisabledPackageReceivers(recordOneAll, pkgToCleanup, UserHandle.USER_ALL);
         assertNull(verifyRemaining(recordOneAll, excludeReceivers(receiversU0, pkgToCleanup, -1)));
 
         // Send to one user, cleanup a package one user.
-        final BroadcastRecord recordOneOne = createBroadcastRecord(receiversU0, user0);
+        final BroadcastRecord recordOneOne = createBroadcastRecord(receiversU0, user0,
+                new Intent());
         cleanupDisabledPackageReceivers(recordOneOne, pkgToCleanup, user0);
         assertNull(verifyRemaining(recordOneOne, excludeReceivers(receiversU0, pkgToCleanup, -1)));
 
         // Without given package (e.g. stop user):
         // Send to all users, cleanup one user.
-        final BroadcastRecord recordAllM = createBroadcastRecord(receiversM, UserHandle.USER_ALL);
+        final BroadcastRecord recordAllM = createBroadcastRecord(receiversM, UserHandle.USER_ALL,
+                new Intent());
         cleanupDisabledPackageReceivers(recordAllM, null /* packageName */, user1);
         assertNull(verifyRemaining(recordAllM,
                 excludeReceivers(receiversM, null /* packageName */, user1)));
 
         // Send to one user, cleanup one user.
-        final BroadcastRecord recordU0 = createBroadcastRecord(receiversU0, user0);
+        final BroadcastRecord recordU0 = createBroadcastRecord(receiversU0, user0, new Intent());
         cleanupDisabledPackageReceivers(recordU0, null /* packageName */, user0);
         assertNull(verifyRemaining(recordU0, Collections.emptyList()));
     }
 
+    // Test defer BOOT_COMPLETED and LOCKED_BOOT_COMPLETED broaddcasts.
+    @Test
+    public void testDeferBootCompletedBroadcast() {
+        testDeferBootCompletedBroadcast_defer_none(ACTION_BOOT_COMPLETED);
+        testDeferBootCompletedBroadcast_defer_all(ACTION_BOOT_COMPLETED);
+        testDeferBootCompletedBroadcast_defer_background_restricted_only(ACTION_BOOT_COMPLETED);
+        testDeferBootCompletedBroadcast_defer_none(ACTION_LOCKED_BOOT_COMPLETED);
+        testDeferBootCompletedBroadcast_defer_all(ACTION_LOCKED_BOOT_COMPLETED);
+        testDeferBootCompletedBroadcast_defer_background_restricted_only(
+                ACTION_LOCKED_BOOT_COMPLETED);
+    }
+
+    // non-BOOT_COMPLETED broadcast does not get deferred.
+    @Test
+    public void testNoDeferOtherBroadcast() {
+        // no split for non-BOOT_COMPLETED broadcasts.
+        final BroadcastRecord br = createBootCompletedBroadcastRecord(ACTION_TIME_CHANGED);
+        final int origReceiversSize = br.receivers.size();
+
+        SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
+                mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_ALL);
+        // No receivers get deferred.
+        assertEquals(0, deferred.size());
+        assertEquals(origReceiversSize, br.receivers.size());
+    }
+
+    private BroadcastRecord createBootCompletedBroadcastRecord(String action) {
+        final List<ResolveInfo> receivers = createReceiverInfos(PACKAGE_LIST, USER_LIST);
+        final BroadcastRecord br = createBroadcastRecord(receivers, UserHandle.USER_ALL,
+                new Intent(action));
+        assertEquals(PACKAGE_LIST.length * USER_LIST.length, br.receivers.size());
+        return br;
+    }
+
+    // Test type DEFER_BOOT_COMPLETED_BROADCAST_NONE, this type does not defer any receiver.
+    private void testDeferBootCompletedBroadcast_defer_none(String action) {
+        final BroadcastRecord br = createBootCompletedBroadcastRecord(action);
+        final int origReceiversSize = br.receivers.size();
+
+        SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
+                mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_NONE);
+        // No receivers get deferred.
+        assertEquals(0, deferred.size());
+        assertEquals(origReceiversSize, br.receivers.size());
+    }
+
+    // Test type DEFER_BOOT_COMPLETED_BROADCAST_ALL, this type defer all receivers.
+    private void testDeferBootCompletedBroadcast_defer_all(String action) {
+        final BroadcastRecord br = createBootCompletedBroadcastRecord(action);
+
+        SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
+                mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_ALL);
+        // original BroadcastRecord receivers list is empty now.
+        assertTrue(br.receivers.isEmpty());
+
+        assertEquals(PACKAGE_LIST.length * USER_LIST.length, deferred.size());
+        for (int i = 0; i < PACKAGE_LIST.length; i++) {
+            for (final int userId : USER_LIST) {
+                final int uid = UserHandle.getUid(userId, getAppId(i));
+                assertTrue(deferred.contains(uid));
+                assertEquals(1, deferred.get(uid).receivers.size());
+                final ResolveInfo info = (ResolveInfo) deferred.get(uid).receivers.get(0);
+                assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
+                assertEquals(uid, info.activityInfo.applicationInfo.uid);
+            }
+        }
+    }
+
+    // Test type DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY,
+    // This type defers receiver whose app package is background restricted.
+    private void testDeferBootCompletedBroadcast_defer_background_restricted_only(String action) {
+        final BroadcastRecord br = createBootCompletedBroadcastRecord(action);
+        final int origReceiversSize = br.receivers.size();
+
+        // First half packages in PACKAGE_LIST, return BACKGROUND_RESTRICTED.
+        for (int i = 0; i < PACKAGE_LIST.length / 2; i++) {
+            for (int u = 0; u < USER_LIST.length; u++) {
+                final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
+                doReturn(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED).when(mActivityManagerInternal)
+                        .getRestrictionLevel(eq(uid));
+            }
+        }
+
+        // the second half packages in PACKAGE_LIST, return not BACKGROUND_RESTRICTED.
+        for (int i = PACKAGE_LIST.length / 2; i < PACKAGE_LIST.length; i++) {
+            for (int u = 0; u < USER_LIST.length; u++) {
+                final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
+                doReturn(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED - 10).when(
+                        mActivityManagerInternal).getRestrictionLevel(eq(uid));
+            }
+        }
+
+        SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
+                mActivityManagerInternal,
+                DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY);
+        // original BroadcastRecord receivers list is half now.
+        assertEquals(origReceiversSize / 2, br.receivers.size());
+        assertEquals(origReceiversSize / 2, deferred.size());
+
+        for (int i = 0; i < PACKAGE_LIST.length / 2; i++) {
+            for (int u = 0; u < USER_LIST.length; u++) {
+                final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
+                assertTrue(deferred.contains(uid));
+                assertEquals(1, deferred.get(uid).receivers.size());
+                final ResolveInfo info = (ResolveInfo) deferred.get(uid).receivers.get(0);
+                assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
+                assertEquals(uid, info.activityInfo.applicationInfo.uid);
+            }
+        }
+
+        for (int i = PACKAGE_LIST.length / 2; i < PACKAGE_LIST.length; i++) {
+            for (int u = 0; u < USER_LIST.length; u++) {
+                final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
+                boolean found = false;
+                for (int r = 0; r < br.receivers.size(); r++) {
+                    final ResolveInfo info = (ResolveInfo) br.receivers.get(r);
+                    if (uid == info.activityInfo.applicationInfo.uid) {
+                        found = true;
+                        break;
+                    }
+                }
+                assertTrue(found);
+            }
+        }
+    }
+
+    /**
+     * Test the class {@link BroadcastDispatcher#DeferredBootCompletedBroadcastPerUser}
+     */
+    @Test
+    public void testDeferBootCompletedBroadcast_dispatcher() {
+        testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_LOCKED_BOOT_COMPLETED, false);
+        testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_BOOT_COMPLETED, false);
+        testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_LOCKED_BOOT_COMPLETED, true);
+        testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_BOOT_COMPLETED, true);
+    }
+
+    private void testDeferBootCompletedBroadcast_dispatcher_internal(String action,
+            boolean isAllUidReady) {
+        final List<ResolveInfo> receivers = createReceiverInfos(PACKAGE_LIST, new int[] {USER0});
+        final BroadcastRecord br = createBroadcastRecord(receivers, USER0, new Intent(action));
+        assertEquals(PACKAGE_LIST.length, br.receivers.size());
+
+        SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
+                mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_ALL);
+        // original BroadcastRecord receivers list is empty now.
+        assertTrue(br.receivers.isEmpty());
+        assertEquals(PACKAGE_LIST.length, deferred.size());
+
+        DeferredBootCompletedBroadcastPerUser deferredPerUser =
+                new DeferredBootCompletedBroadcastPerUser(USER0);
+        deferredPerUser.enqueueBootCompletedBroadcasts(action, deferred);
+
+        if (action.equals(ACTION_LOCKED_BOOT_COMPLETED)) {
+            assertEquals(PACKAGE_LIST.length,
+                    deferredPerUser.mDeferredLockedBootCompletedBroadcasts.size());
+            assertTrue(deferredPerUser.mLockedBootCompletedBroadcastReceived);
+            for (int i = 0; i < PACKAGE_LIST.length; i++) {
+                final int uid = UserHandle.getUid(USER0, getAppId(i));
+                if (!isAllUidReady) {
+                    deferredPerUser.updateUidReady(uid);
+                }
+                BroadcastRecord d = deferredPerUser.dequeueDeferredBootCompletedBroadcast(
+                        isAllUidReady);
+                final ResolveInfo info = (ResolveInfo) d.receivers.get(0);
+                assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
+                assertEquals(uid, info.activityInfo.applicationInfo.uid);
+            }
+            assertEquals(0, deferredPerUser.mUidReadyForLockedBootCompletedBroadcast.size());
+        } else if (action.equals(ACTION_BOOT_COMPLETED)) {
+            assertEquals(PACKAGE_LIST.length,
+                    deferredPerUser.mDeferredBootCompletedBroadcasts.size());
+            assertTrue(deferredPerUser.mBootCompletedBroadcastReceived);
+            for (int i = 0; i < PACKAGE_LIST.length; i++) {
+                final int uid = UserHandle.getUid(USER0, getAppId(i));
+                if (!isAllUidReady) {
+                    deferredPerUser.updateUidReady(uid);
+                }
+                BroadcastRecord d = deferredPerUser.dequeueDeferredBootCompletedBroadcast(
+                        isAllUidReady);
+                final ResolveInfo info = (ResolveInfo) d.receivers.get(0);
+                assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
+                assertEquals(uid, info.activityInfo.applicationInfo.uid);
+            }
+            assertEquals(0, deferredPerUser.mUidReadyForBootCompletedBroadcast.size());
+        }
+    }
+
     private static void cleanupDisabledPackageReceivers(BroadcastRecord record,
             String packageName, int userId) {
         record.cleanupDisabledPackageReceiversLocked(packageName, null /* filterByClasses */,
@@ -148,7 +378,7 @@
         for (int i = 0; i < packages.length; i++) {
             for (final int userId : userIds) {
                 receivers.add(createResolveInfo(packages[i],
-                        UserHandle.getUid(userId, Process.FIRST_APPLICATION_UID + i)));
+                        UserHandle.getUid(userId, getAppId(i))));
             }
         }
         return receivers;
@@ -172,10 +402,11 @@
         return excludedList;
     }
 
-    private static BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId) {
+    private static BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
+            Intent intent) {
         return new BroadcastRecord(
                 null /* queue */,
-                new Intent(),
+                intent,
                 null /* callerApp */,
                 null  /* callerPackage */,
                 null /* callerFeatureId */,
@@ -200,4 +431,8 @@
                 null /* activityStartsToken */,
                 false /* timeoutExempt */ );
     }
+
+    private static int getAppId(int i) {
+        return Process.FIRST_APPLICATION_UID + i;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 69d8e89..b94b690 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -235,7 +235,8 @@
     @Test
     public void testAuthenticate_credentialAllowedButNotSetup_returnsNoDeviceCredential()
             throws Exception {
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(false);
 
         mBiometricService = new BiometricService(mContext, mInjector);
         mBiometricService.onStart();
@@ -254,7 +255,8 @@
         // When no biometrics are enrolled, but credentials are set up, status bar should be
         // invoked right away with showAuthenticationDialog
 
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(true);
 
         mBiometricService = new BiometricService(mContext, mInjector);
         mBiometricService.onStart();
@@ -532,7 +534,8 @@
     public void testAuthenticate_noBiometrics_credentialAllowed() throws Exception {
         setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
         when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false);
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(true);
         invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
                 true /* requireConfirmation */,
                 Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
@@ -601,7 +604,8 @@
     public void testAuthenticate_no_Biometrics_noCredential() throws Exception {
         setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
         when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false);
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(false);
 
         invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
                 true /* requireConfirmation */,
@@ -872,7 +876,8 @@
     @Test
     public void testBiometricOrCredentialAuth_whenBiometricLockout_showsCredential()
             throws Exception {
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(true);
         setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
         when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
                 .thenReturn(LockoutTracker.LOCKOUT_PERMANENT);
@@ -1168,13 +1173,15 @@
         setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
 
         // When only biometric is requested, and sensor is not strong enough
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(false);
         int authenticators = Authenticators.BIOMETRIC_STRONG;
         assertEquals(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
                 invokeCanAuthenticate(mBiometricService, authenticators));
 
         // When credential and biometric are requested, and sensor is not strong enough
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(true);
         authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
         assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
                 invokeCanAuthenticate(mBiometricService, authenticators));
@@ -1186,12 +1193,14 @@
         mBiometricService.onStart();
 
         // Credential requested but not set up
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(false);
         assertEquals(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED,
                 invokeCanAuthenticate(mBiometricService, Authenticators.DEVICE_CREDENTIAL));
 
         // Credential requested and set up
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(true);
         assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
                 invokeCanAuthenticate(mBiometricService, Authenticators.DEVICE_CREDENTIAL));
     }
@@ -1199,7 +1208,8 @@
     @Test
     public void testCanAuthenticate_whenNoBiometricsEnrolled() throws Exception {
         // With credential set up, test the following.
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(true);
         setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
                 false /* enrolled */);
 
@@ -1218,7 +1228,8 @@
     public void testCanAuthenticate_whenBiometricsNotEnabledForApps() throws Exception {
         setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
         when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false);
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(true);
 
         // When only biometric is requested
         int authenticators = Authenticators.BIOMETRIC_STRONG;
@@ -1247,7 +1258,8 @@
                 invokeCanAuthenticate(mBiometricService, authenticators));
 
         // When credential and biometric are requested, and credential is set up
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(true);
         assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
                 invokeCanAuthenticate(mBiometricService, authenticators));
     }
@@ -1394,7 +1406,8 @@
         // Requesting strong and credential, when credential is setup
         resetReceivers();
         authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(true);
         assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
                 invokeCanAuthenticate(mBiometricService, authenticators));
         requestId = invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
@@ -1527,7 +1540,8 @@
     public void testWorkAuthentication_fingerprintFailsIfDisabledByDevicePolicyManager()
             throws Exception {
         setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
-        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+        when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+                .thenReturn(true);
         when(mDevicePolicyManager
                 .getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
                 .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
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 48e2122..fe110e5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -55,6 +55,7 @@
 import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
 import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
 import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_PROFILE_OFF_DEADLINE;
 import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_TURN_PROFILE_ON_NOTIFICATION;
@@ -997,7 +998,8 @@
         addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
 
         // Change the parent user's password.
-        dpm.reportPasswordChanged(UserHandle.USER_SYSTEM);
+        dpm.reportPasswordChanged(new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD),
+                UserHandle.USER_SYSTEM);
 
         // The managed profile owner should receive this broadcast.
         final Intent intent = new Intent(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED);
@@ -1036,7 +1038,8 @@
         addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
 
         // Change the profile's password.
-        dpm.reportPasswordChanged(MANAGED_PROFILE_USER_ID);
+        dpm.reportPasswordChanged(new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD),
+                MANAGED_PROFILE_USER_ID);
 
         // Both the device owner and the managed profile owner should receive this broadcast.
         final Intent intent = new Intent(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED);
@@ -5531,7 +5534,7 @@
 
         reset(mContext.spiedContext);
         // This should be ignored, as there is no lock screen.
-        dpm.reportPasswordChanged(userHandle);
+        dpm.reportPasswordChanged(new PasswordMetrics(CREDENTIAL_TYPE_NONE), userHandle);
 
         // No broadcast should be sent.
         verify(mContext.spiedContext, times(0)).sendBroadcastAsUser(
@@ -5904,7 +5907,7 @@
 
         when(getServices().lockSettingsInternal.getUserPasswordMetrics(userHandle))
                 .thenReturn(passwordMetrics);
-        dpm.reportPasswordChanged(userHandle);
+        dpm.reportPasswordChanged(passwordMetrics, userHandle);
 
         verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 58e1c43..eae9ee7f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -257,7 +257,7 @@
         flushHandlerTasks();
         final PasswordMetrics metric = PasswordMetrics.computeForCredential(pattern);
         assertEquals(metric, mService.getUserPasswordMetrics(PRIMARY_USER_ID));
-        verify(mDevicePolicyManager).reportPasswordChanged(PRIMARY_USER_ID);
+        verify(mDevicePolicyManager).reportPasswordChanged(metric, PRIMARY_USER_ID);
 
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
                 pattern, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 13a8f69..b72b8d2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -30,15 +30,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
 import android.content.pm.UserInfo;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedActivityImpl;
-import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
-import com.android.server.pm.pkg.component.ParsedProviderImpl;
 import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
@@ -53,6 +48,13 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedActivityImpl;
+import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
+import com.android.server.pm.pkg.component.ParsedProviderImpl;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
 import com.android.server.utils.WatchableTester;
 
 import org.junit.Before;
@@ -101,6 +103,8 @@
     AppsFilter.StateProvider mStateProvider;
     @Mock
     Executor mMockExecutor;
+    @Mock
+    PackageManagerInternal mMockPmInternal;
 
     private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>();
 
@@ -222,7 +226,7 @@
     public void testSystemReadyPropogates() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         appsFilter.onSystemReady();
@@ -234,7 +238,7 @@
     public void testQueriesAction_FilterMatches() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -257,7 +261,7 @@
     public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         final Signature frameworkSignature = Mockito.mock(Signature.class);
@@ -306,7 +310,7 @@
     public void testQueriesProvider_FilterMatches() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -331,7 +335,7 @@
     public void testOnUserUpdated_FilterMatches() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
 
         appsFilter.onSystemReady();
@@ -389,7 +393,7 @@
     public void testQueriesDifferentProvider_Filters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -414,7 +418,7 @@
     public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -433,7 +437,7 @@
     public void testQueriesAction_NoMatchingAction_Filters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -450,7 +454,7 @@
     public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -471,7 +475,7 @@
     public void testNoQueries_Filters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -488,7 +492,7 @@
     public void testNoUsesLibrary_Filters() throws Exception {
         final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
                 new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
-                mMockExecutor);
+                mMockExecutor, mMockPmInternal);
 
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
@@ -514,7 +518,7 @@
     public void testUsesLibrary_DoesntFilter() throws Exception {
         final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
                 new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
-                mMockExecutor);
+                mMockExecutor, mMockPmInternal);
 
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
@@ -541,7 +545,7 @@
     public void testUsesOptionalLibrary_DoesntFilter() throws Exception {
         final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
                 new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
-                mMockExecutor);
+                mMockExecutor, mMockPmInternal);
 
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
@@ -568,7 +572,7 @@
     public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception {
         final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
                 new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
-                mMockExecutor);
+                mMockExecutor, mMockPmInternal);
 
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
@@ -600,7 +604,7 @@
     public void testForceQueryable_SystemDoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -619,7 +623,7 @@
     public void testForceQueryable_NonSystemFilters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -636,7 +640,7 @@
     public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"},
-                        false, null, mMockExecutor);
+                        false, null, mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -655,7 +659,7 @@
     public void testSystemSignedTarget_DoesntFilter() throws CertificateException {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         appsFilter.onSystemReady();
 
         final Signature frameworkSignature = Mockito.mock(Signature.class);
@@ -684,7 +688,7 @@
     public void testForceQueryableByDevice_NonSystemCaller_Filters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"},
-                        false, null, mMockExecutor);
+                        false, null, mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -702,7 +706,8 @@
     public void testSystemQueryable_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{},
-                        true /* system force queryable */, null, mMockExecutor);
+                        true /* system force queryable */, null, mMockExecutor,
+                        mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -720,7 +725,7 @@
     public void testQueriesPackage_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -739,7 +744,7 @@
                 .thenReturn(false);
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -756,7 +761,7 @@
     public void testSystemUid_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -772,7 +777,7 @@
     public void testSystemUidSecondaryUser_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -789,7 +794,7 @@
     public void testNonSystemUid_NoCallingSetting_Filters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -804,7 +809,7 @@
     public void testNoTargetPackage_filters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -862,7 +867,7 @@
                         return Collections.emptyMap();
                     }
                 },
-                mMockExecutor);
+                mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -918,7 +923,16 @@
                 .setOverlayTargetOverlayableName("overlayableName");
         ParsingPackage actorOne = pkg("com.some.package.actor.one");
         ParsingPackage actorTwo = pkg("com.some.package.actor.two");
-
+        ArraySet<PackageStateInternal> actorSharedSettingPackages = new ArraySet<>();
+        PackageSetting ps1 = getPackageSettingFromParsingPackage(actorOne, DUMMY_ACTOR_APPID,
+                null /*settingBuilder*/);
+        PackageSetting ps2 = getPackageSettingFromParsingPackage(actorTwo, DUMMY_ACTOR_APPID,
+                null /*settingBuilder*/);
+        actorSharedSettingPackages.add(ps1);
+        actorSharedSettingPackages.add(ps2);
+        when(mMockPmInternal.getSharedUserPackages(any(Integer.class))).thenReturn(
+                actorSharedSettingPackages
+        );
         final AppsFilter appsFilter = new AppsFilter(
                 mStateProvider,
                 mFeatureConfigMock,
@@ -949,20 +963,18 @@
                         return Collections.emptyMap();
                     }
                 },
-                mMockExecutor);
+                mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
         PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID);
         SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser",
                 targetSetting.getFlags(), targetSetting.getPrivateFlags());
+        actorSharedSetting.mAppId = 100; /* mimic a valid sharedUserSetting.mAppId */
         PackageSetting overlaySetting =
                 simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_APPID);
-        simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_APPID,
-                null /*settingBuilder*/, actorSharedSetting);
-        simulateAddPackage(appsFilter, actorTwo, DUMMY_ACTOR_APPID,
-                null /*settingBuilder*/, actorSharedSetting);
-
+        simulateAddPackage(ps1, appsFilter, actorSharedSetting);
+        simulateAddPackage(ps2, appsFilter, actorSharedSetting);
 
         // actorTwo can see both target and overlay
         assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSharedSetting,
@@ -975,7 +987,7 @@
     public void testInitiatingApp_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -993,7 +1005,7 @@
     public void testUninstalledInitiatingApp_Filters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -1011,7 +1023,7 @@
     public void testOriginatingApp_Filters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -1036,7 +1048,7 @@
     public void testInstallingApp_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -1061,7 +1073,7 @@
     public void testInstrumentation_DoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -1090,7 +1102,7 @@
     public void testWhoCanSee() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -1163,7 +1175,7 @@
     public void testOnChangeReport() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -1236,7 +1248,7 @@
     public void testOnChangeReportedFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor);
+                        mMockExecutor, mMockPmInternal);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange filter");
@@ -1292,8 +1304,15 @@
     private PackageSetting simulateAddPackage(AppsFilter filter,
                 ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action,
             @Nullable SharedUserSetting sharedUserSetting) {
-        AndroidPackage newPkg = ((ParsedPackage) newPkgBuilder.hideAsParsed()).hideAsFinal();
+        final PackageSetting setting =
+                getPackageSettingFromParsingPackage(newPkgBuilder, appId, action);
+        simulateAddPackage(setting, filter, sharedUserSetting);
+        return setting;
+    }
 
+    private PackageSetting getPackageSettingFromParsingPackage(ParsingPackage newPkgBuilder,
+            int appId, @Nullable WithSettingBuilder action) {
+        AndroidPackage newPkg = ((ParsedPackage) newPkgBuilder.hideAsParsed()).hideAsFinal();
         final PackageSettingBuilder settingBuilder = new PackageSettingBuilder()
                 .setPackage(newPkg)
                 .setAppId(appId)
@@ -1302,13 +1321,17 @@
                 .setPVersionCode(1L);
         final PackageSetting setting =
                 (action == null ? settingBuilder : action.withBuilder(settingBuilder)).build();
-        mExisting.put(newPkg.getPackageName(), setting);
+        return setting;
+    }
+
+    private void simulateAddPackage(PackageSetting setting, AppsFilter filter,
+            @Nullable SharedUserSetting sharedUserSetting) {
+        mExisting.put(setting.getPackageName(), setting);
         if (sharedUserSetting != null) {
             sharedUserSetting.addPackage(setting);
-            setting.setSharedUser(sharedUserSetting);
+            setting.setSharedUserAppId(sharedUserSetting.mAppId);
         }
         filter.addPackage(setting);
-        return setting;
     }
 
     private WithSettingBuilder withInstallSource(String initiatingPackageName,
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 ed4dee1..345f22f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -722,6 +722,7 @@
         Settings.updatePackageSetting(
                 testPkgSetting01,
                 null /*disabledPkg*/,
+                null /*existingSharedUserSetting*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
                 null /*legacyNativeLibraryPath*/,
@@ -757,6 +758,7 @@
         Settings.updatePackageSetting(
                 testPkgSetting01,
                 null /*disabledPkg*/,
+                null /*existingSharedUserSetting*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
                 null /*legacyNativeLibraryPath*/,
@@ -792,6 +794,7 @@
             Settings.updatePackageSetting(
                     testPkgSetting01,
                     null /*disabledPkg*/,
+                    null /*existingSharedUserSetting*/,
                     testUserSetting01 /*sharedUser*/,
                     UPDATED_CODE_PATH /*codePath*/,
                     null /*legacyNativeLibraryPath*/,
@@ -1148,8 +1151,6 @@
         assertThat(origPkgSetting.getRealName(), is(testPkgSetting.getRealName()));
         assertSame(origPkgSetting.getSecondaryCpuAbi(), testPkgSetting.getSecondaryCpuAbi());
         assertThat(origPkgSetting.getSecondaryCpuAbi(), is(testPkgSetting.getSecondaryCpuAbi()));
-        assertSame(origPkgSetting.getSharedUser(), testPkgSetting.getSharedUser());
-        assertThat(origPkgSetting.getSharedUser(), is(testPkgSetting.getSharedUser()));
         assertSame(origPkgSetting.getSignatures(), testPkgSetting.getSignatures());
         assertThat(origPkgSetting.getSignatures(), is(testPkgSetting.getSignatures()));
         assertThat(origPkgSetting.getLastModifiedTime(), is(testPkgSetting.getLastModifiedTime()));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
index 54bfe01..9962a3c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
@@ -26,6 +26,7 @@
 class ScanRequestBuilder {
     private final ParsedPackage mPkg;
     private AndroidPackage mOldPkg;
+    private SharedUserSetting mOldSharedUserSetting;
     private SharedUserSetting mSharedUserSetting;
     private PackageSetting mPkgSetting;
     private PackageSetting mDisabledPkgSetting;
@@ -52,6 +53,11 @@
         return this;
     }
 
+    public ScanRequestBuilder setOldSharedUserSetting(SharedUserSetting oldSharedUserSetting) {
+        this.mOldSharedUserSetting = oldSharedUserSetting;
+        return this;
+    }
+
     public ScanRequestBuilder setPkgSetting(PackageSetting pkgSetting) {
         this.mPkgSetting = pkgSetting;
         return this;
@@ -110,8 +116,8 @@
 
     ScanRequest build() {
         return new ScanRequest(
-                mPkg, mSharedUserSetting, mOldPkg, mPkgSetting, mDisabledPkgSetting,
-                mOriginalPkgSetting, mRealPkgName, mParseFlags, mScanFlags, mIsPlatformPackage,
-                mUser, mCpuAbiOverride);
+                mPkg, mOldSharedUserSetting, mOldPkg, mPkgSetting, mSharedUserSetting,
+                mDisabledPkgSetting, mOriginalPkgSetting, mRealPkgName, mParseFlags, mScanFlags,
+                mIsPlatformPackage, mUser, mCpuAbiOverride);
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index a3440b4..b71323b4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.notification;
 
+import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
@@ -252,6 +253,39 @@
     }
 
     @Test
+    public void testSetNotificationPermission_pkgPerm_notUserSet_grantedByDefaultPermNotSet()
+            throws Exception {
+        when(mPermManager.getPermissionFlags(anyString(),
+                eq(Manifest.permission.POST_NOTIFICATIONS),
+                anyInt())).thenReturn(FLAG_PERMISSION_GRANTED_BY_DEFAULT);
+        PermissionHelper.PackagePermission pkgPerm = new PermissionHelper.PackagePermission(
+                "pkg", 10, true, false);
+
+        mPermissionHelper.setNotificationPermission(pkgPerm);
+        verify(mPermManager, never()).revokeRuntimePermission(
+                anyString(), anyString(), anyInt(), anyString());
+        verify(mPermManager, never()).updatePermissionFlags(
+                anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt());
+    }
+
+    @Test
+    public void testSetNotificationPermission_pkgPerm_userSet_grantedByDefaultPermSet()
+            throws Exception {
+        when(mPermManager.getPermissionFlags(anyString(),
+                eq(Manifest.permission.POST_NOTIFICATIONS),
+                anyInt())).thenReturn(FLAG_PERMISSION_GRANTED_BY_DEFAULT);
+        PermissionHelper.PackagePermission pkgPerm = new PermissionHelper.PackagePermission(
+                "pkg", 10, true, true);
+
+        mPermissionHelper.setNotificationPermission(pkgPerm);
+        verify(mPermManager).grantRuntimePermission(
+                "pkg", Manifest.permission.POST_NOTIFICATIONS, 10);
+        verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
+                FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_REVIEW_REQUIRED,
+                FLAG_PERMISSION_USER_SET, true, 10);
+    }
+
+    @Test
     public void testSetNotificationPermission_revokeUserSet() throws Exception {
         mPermissionHelper.setNotificationPermission("pkg", 10, false, true);
 
@@ -283,6 +317,32 @@
     }
 
     @Test
+    public void testSetNotificationPermission_SystemFixedPermNotSet() throws Exception {
+        when(mPermManager.getPermissionFlags(anyString(),
+                eq(Manifest.permission.POST_NOTIFICATIONS),
+                anyInt())).thenReturn(FLAG_PERMISSION_SYSTEM_FIXED);
+
+        mPermissionHelper.setNotificationPermission("pkg", 10, false, true);
+        verify(mPermManager, never()).revokeRuntimePermission(
+                anyString(), anyString(), anyInt(), anyString());
+        verify(mPermManager, never()).updatePermissionFlags(
+                anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt());
+    }
+
+    @Test
+    public void testSetNotificationPermission_PolicyFixedPermNotSet() throws Exception {
+        when(mPermManager.getPermissionFlags(anyString(),
+                eq(Manifest.permission.POST_NOTIFICATIONS),
+                anyInt())).thenReturn(FLAG_PERMISSION_POLICY_FIXED);
+
+        mPermissionHelper.setNotificationPermission("pkg", 10, false, true);
+        verify(mPermManager, never()).revokeRuntimePermission(
+                anyString(), anyString(), anyInt(), anyString());
+        verify(mPermManager, never()).updatePermissionFlags(
+                anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt());
+    }
+
+    @Test
     public void testIsPermissionFixed() throws Exception {
         when(mPermManager.getPermissionFlags(anyString(),
                 eq(Manifest.permission.POST_NOTIFICATIONS),
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 fc4ab22..8f1eed8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -789,6 +789,73 @@
     }
 
     @Test
+    public void testReadXml_oldXml_migration_NoUid() throws Exception {
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+
+        when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
+        String xml = "<ranking version=\"2\">\n"
+                + "<package name=\"something\" show_badge=\"true\">\n"
+                + "<channel id=\"idn\" name=\"name\" importance=\"2\"/>\n"
+                + "</package>\n"
+                + "</ranking>\n";
+        NotificationChannel idn = new NotificationChannel("idn", "name", IMPORTANCE_LOW);
+        idn.setSound(null, new AudioAttributes.Builder()
+                .setUsage(USAGE_NOTIFICATION)
+                .setContentType(CONTENT_TYPE_SONIFICATION)
+                .setFlags(0)
+                .build());
+        idn.setShowBadge(false);
+
+        loadByteArrayXml(xml.getBytes(), true, USER_SYSTEM);
+        verify(mPermissionHelper, never()).setNotificationPermission(any());
+
+        when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(1234);
+        final ApplicationInfo app = new ApplicationInfo();
+        app.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
+        when(mPm.getApplicationInfoAsUser(eq("something"), anyInt(), anyInt())).thenReturn(app);
+
+        mHelper.onPackagesChanged(false, 0, new String[] {"something"}, new int[] {1234});
+
+
+        verify(mPermissionHelper, times(1)).setNotificationPermission(any());
+    }
+
+    @Test
+    public void testReadXml_newXml_noMigration_NoUid() throws Exception {
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+
+        when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
+        String xml = "<ranking version=\"3\">\n"
+                + "<package name=\"something\" show_badge=\"true\">\n"
+                + "<channel id=\"idn\" name=\"name\" importance=\"2\"/>\n"
+                + "</package>\n"
+                + "</ranking>\n";
+        NotificationChannel idn = new NotificationChannel("idn", "name", IMPORTANCE_LOW);
+        idn.setSound(null, new AudioAttributes.Builder()
+                .setUsage(USAGE_NOTIFICATION)
+                .setContentType(CONTENT_TYPE_SONIFICATION)
+                .setFlags(0)
+                .build());
+        idn.setShowBadge(false);
+
+        loadByteArrayXml(xml.getBytes(), true, USER_SYSTEM);
+
+        when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(1234);
+        final ApplicationInfo app = new ApplicationInfo();
+        app.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
+        when(mPm.getApplicationInfoAsUser(eq("something"), anyInt(), anyInt())).thenReturn(app);
+
+        mHelper.onPackagesChanged(false, 0, new String[] {"something"}, new int[] {1234});
+
+
+        verify(mPermissionHelper, never()).setNotificationPermission(any());
+    }
+
+    @Test
     public void testChannelXmlForNonBackup_postMigration() throws Exception {
         when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 1285a84..8cbbe94 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -263,6 +263,25 @@
         }
 
         @Override
+        public String getVoiceInteractorPackageName(IBinder callingVoiceInteractor) {
+            VoiceInteractionManagerServiceImpl impl =
+                    VoiceInteractionManagerService.this.mServiceStub.mImpl;
+            if (impl == null) {
+                return null;
+            }
+            VoiceInteractionSessionConnection session =
+                    impl.mActiveSession;
+            if (session == null) {
+                return null;
+            }
+            IVoiceInteractor voiceInteractor = session.mInteractor;
+            if (voiceInteractor == null || voiceInteractor.asBinder() != callingVoiceInteractor) {
+                return null;
+            }
+            return session.mSessionComponentName.getPackageName();
+        }
+
+        @Override
         public HotwordDetectionServiceIdentity getHotwordDetectionServiceIdentity() {
             // IMPORTANT: This is called when performing permission checks; do not lock!
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 03be19f..2c39863 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3796,7 +3796,7 @@
     /**
      * SMDP+ server address for downloading opportunistic eSIM profile.
      * FQDN (Fully Qualified Domain Name) of the SM-DP+ (e.g., smdp.gsma.com) restricted to the
-     * Alphanumeric mode character set defined in table 5 of ISO/IEC 18004 [15] excluding '$'.
+     * Alphanumeric mode character set defined in table 5 of ISO/IEC 18004 excluding '$'.
      */
     public static final String KEY_SMDP_SERVER_ADDRESS_STRING =
             "smdp_server_address_string";
@@ -3840,6 +3840,17 @@
             "opportunistic_carrier_ids_int_array";
 
     /**
+     * Boolean configuration to control auto provisioning eSIM download in
+     * OpportunisticNetworkService using only WiFi or both WiFi/Data.
+     * True will download esim only via WiFi.
+     * False will use both WiFi and Data connection.
+     *
+     * @hide
+     */
+    public static final String KEY_OPPORTUNISTIC_ESIM_DOWNLOAD_VIA_WIFI_ONLY_BOOL =
+            "opportunistic_esim_download_via_wifi_only_bool";
+
+    /**
      * Controls RSRP threshold at which OpportunisticNetworkService will decide whether
      * the opportunistic network is good enough for internet data.
      */
@@ -8826,6 +8837,7 @@
         sDefaults.putInt(KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT, 5);
         sDefaults.putInt(KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT, 60);
         sDefaults.putIntArray(KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY, new int[] {0});
+        sDefaults.putBoolean(KEY_OPPORTUNISTIC_ESIM_DOWNLOAD_VIA_WIFI_ONLY_BOOL, false);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
         sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.bp b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.bp
new file mode 100644
index 0000000..4fff969
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2013 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "frameworks_base_license",
+    ],
+}
+
+android_test {
+    name: "SmartCamera",
+    optimize: {
+        enabled: false,
+    },
+    // comment it out for now since we need use some hidden APIs
+    sdk_version: "current",
+    static_libs: ["android-ex-camera2"],
+    srcs: [
+        "src/**/*.java",
+    ],
+    jni_libs: ["libsmartcamera_jni"],
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk
deleted file mode 100644
index 6003628..0000000
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright (C) 2013 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.
-
-ifneq ($(TARGET_BUILD_JAVA_SUPPORT_LEVEL),)
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-# comment it out for now since we need use some hidden APIs
-LOCAL_SDK_VERSION := current
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-renderscript-files-under, src)
-
-LOCAL_PACKAGE_NAME := SmartCamera
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../NOTICE
-LOCAL_JNI_SHARED_LIBRARIES := libsmartcamera_jni
-
-include $(BUILD_PACKAGE)
-
-# Include packages in subdirectories
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
-endif
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.bp b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.bp
new file mode 100644
index 0000000..5edb1de
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2013 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "frameworks_base_license",
+    ],
+}
+
+android_test {
+    name: "SmartCamera-tests",
+    platform_apis: true,
+    srcs: ["src/**/*.java"],
+    libs: ["android.test.base"],
+    static_libs: [
+        "guava",
+        "junit",
+    ],
+    optimize: {
+        enabled: false,
+    },
+    instrumentation_for: "SmartCamera",
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
deleted file mode 100644
index c23d593..0000000
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (C) 2013 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-# LOCAL_SDK_VERSION := current
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_PACKAGE_NAME := SmartCamera-tests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_SRC_FILES += $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := guava junit
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_INSTRUMENTATION_FOR := SmartCamera
-
-include $(BUILD_PACKAGE)
diff --git a/tests/CanvasCompare/Android.bp b/tests/CanvasCompare/Android.bp
new file mode 100644
index 0000000..9883115
--- /dev/null
+++ b/tests/CanvasCompare/Android.bp
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "frameworks_base_license",
+    ],
+}
+
+android_test {
+    name: "CanvasCompare",
+    srcs: [
+        "src/**/*.java",
+        ":CanvasCompare-rscript{CanvasCompare.srcjar}",
+    ],
+    resource_zips: [
+        ":CanvasCompare-rscript{CanvasCompare.res.zip}",
+    ],
+    platform_apis: true,
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: ["junit"],
+}
+
+genrule {
+    name: "CanvasCompare-rscript",
+    srcs: [
+        "src/**/*.rscript",
+        ":rs_script_api",
+        ":rs_clang_headers",
+    ],
+    tools: [
+        "llvm-rs-cc",
+        "soong_zip",
+    ],
+    out: [
+        "CanvasCompare.srcjar",
+        "CanvasCompare.res.zip",
+    ],
+    cmd: "for f in $(locations src/**/*.rscript); do " +
+        "  $(location llvm-rs-cc) -o $(genDir)/res/raw -p $(genDir)/src " +
+        "  -I $$(dirname $$(echo $(locations :rs_script_api) | awk '{ print $$1 }')) " +
+        "  -I $$(dirname $$(echo $(locations :rs_clang_headers) | awk '{ print $$1 }')) $${f}; " +
+        "done && " +
+        "$(location soong_zip) -srcjar -o $(location CanvasCompare.srcjar) -C $(genDir)/src -D $(genDir)/src &&" +
+        "$(location soong_zip) -o $(location CanvasCompare.res.zip) -C $(genDir)/res -D $(genDir)/res",
+}
diff --git a/tests/CanvasCompare/Android.mk b/tests/CanvasCompare/Android.mk
deleted file mode 100644
index b82ae65..0000000
--- a/tests/CanvasCompare/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_PACKAGE_NAME := CanvasCompare
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../NOTICE
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-
-include $(BUILD_PACKAGE)