Merge "Camera: Switch the reader callback when the sequence ends" into sc-dev
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 8723515..8fcd2f9 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -146,19 +146,17 @@
      * <p>It is a no-op to set the same schema as has been previously set; this is handled
      * efficiently.
      *
-     * <p>By default, documents are visible on platform surfaces. To opt out, call {@code
-     * SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any
-     * visibility settings apply only to the schemas that are included in the {@code request}.
-     * Visibility settings for a schema type do not apply or persist across
-     * {@link SetSchemaRequest}s.
+     * <p>By default, documents are visible on platform surfaces. To opt out, call
+     * {@link SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} with {@code visible} as
+     * false. Any visibility settings apply only to the schemas that are included in the
+     * {@code request}. Visibility settings for a schema type do not persist across
+     * {@link #setSchema} calls.
      *
      * @param request  The schema update request.
      * @param executor Executor on which to invoke the callback.
      * @param callback Callback to receive errors resulting from setting the schema. If the
      *                 operation succeeds, the callback will be invoked with {@code null}.
      */
-    // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
-    //  exposed.
     public void setSchema(
             @NonNull SetSchemaRequest request,
             @NonNull @CallbackExecutor Executor executor,
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index c369801..a45fa39 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -41,6 +41,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -58,8 +59,11 @@
     private PackageManagerInternal mPackageManagerInternal;
     private ImplInstanceManager mImplInstanceManager;
 
-    // Cache of unlocked user ids so we don't have to query UserManager service each time.
-    private final Set<Integer> mUnlockedUserIds = new ArraySet<>();
+    // Cache of unlocked user ids so we don't have to query UserManager service each time. The
+    // "locked" suffix refers to the fact that access to the field should be locked; unrelated to
+    // the unlocked status of user ids.
+    @GuardedBy("mUnlockedUserIdsLocked")
+    private final Set<Integer> mUnlockedUserIdsLocked = new ArraySet<>();
 
     public AppSearchManagerService(Context context) {
         super(context);
@@ -74,7 +78,9 @@
 
     @Override
     public void onUserUnlocked(@NonNull TargetUser user) {
-        mUnlockedUserIds.add(user.getUserIdentifier());
+        synchronized (mUnlockedUserIdsLocked) {
+            mUnlockedUserIdsLocked.add(user.getUserIdentifier());
+        }
     }
 
     private class Stub extends IAppSearchManager.Stub {
@@ -503,9 +509,11 @@
         }
 
         private void verifyUserUnlocked(int callingUserId) {
-            if (!mUnlockedUserIds.contains(callingUserId)) {
-                throw new IllegalStateException(
-                        "User " + callingUserId + " is locked or not running.");
+            synchronized (mUnlockedUserIdsLocked) {
+                if (!mUnlockedUserIdsLocked.contains(callingUserId)) {
+                    throw new IllegalStateException(
+                            "User " + callingUserId + " is locked or not running.");
+                }
             }
         }
 
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index 5ea2a02..82319d4 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -29,6 +29,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.appsearch.external.localstorage.AppSearchImpl;
 
 import java.io.File;
@@ -43,7 +44,9 @@
 
     private static ImplInstanceManager sImplInstanceManager;
 
-    private final SparseArray<AppSearchImpl> mInstances = new SparseArray<>();
+    @GuardedBy("mInstancesLocked")
+    private final SparseArray<AppSearchImpl> mInstancesLocked = new SparseArray<>();
+
     private final String mGlobalQuerierPackage;
 
     private ImplInstanceManager(@NonNull String globalQuerierPackage) {
@@ -81,19 +84,16 @@
      * @return An initialized {@link AppSearchImpl} for this user
      */
     @NonNull
-    public AppSearchImpl getAppSearchImpl(@NonNull Context context, @UserIdInt int userId)
-            throws AppSearchException {
-        AppSearchImpl instance = mInstances.get(userId);
-        if (instance == null) {
-            synchronized (ImplInstanceManager.class) {
-                instance = mInstances.get(userId);
-                if (instance == null) {
-                    instance = createImpl(context, userId);
-                    mInstances.put(userId, instance);
-                }
+    public AppSearchImpl getAppSearchImpl(
+            @NonNull Context context, @UserIdInt int userId) throws AppSearchException {
+        synchronized (mInstancesLocked) {
+            AppSearchImpl instance = mInstancesLocked.get(userId);
+            if (instance == null) {
+                instance = createImpl(context, userId);
+                mInstancesLocked.put(userId, instance);
             }
+            return instance;
         }
-        return instance;
     }
 
     private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
index 64dc972..babcd25 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
@@ -332,10 +332,8 @@
         for (Map.Entry<String, List<PackageIdentifier>> entry :
                 schemasPackageAccessible.entrySet()) {
             for (int i = 0; i < entry.getValue().size(); i++) {
-                // TODO(b/169883602): remove the "placeholder" uri once upstream changes to relax
-                // nested document uri rules gets synced down.
                 GenericDocument packageAccessibleDocument =
-                        new GenericDocument.Builder(/*uri=*/ "placeholder", PACKAGE_ACCESSIBLE_TYPE)
+                        new GenericDocument.Builder(/*uri=*/"", PACKAGE_ACCESSIBLE_TYPE)
                                 .setNamespace(NAMESPACE)
                                 .setPropertyString(
                                         PACKAGE_NAME_PROPERTY,
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
index 8e62c0e..b2ffd5b 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
@@ -86,11 +86,11 @@
      * <p>It is a no-op to set the same schema as has been previously set; this is handled
      * efficiently.
      *
-     * <p>By default, documents are visible on platform surfaces. To opt out, call {@code
-     * SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any
-     * visibility settings apply only to the schemas that are included in the {@code request}.
-     * Visibility settings for a schema type do not apply or persist across {@link
-     * SetSchemaRequest}s.
+     * <p>By default, documents are visible on platform surfaces. To opt out, call
+     * {@link SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} with {@code visible} as
+     * false. Any visibility settings apply only to the schemas that are included in the
+     * {@code request}. Visibility settings for a schema type do not persist across
+     * {@link #setSchema} calls.
      *
      * <p>Migration: make non-backwards-compatible changes will delete all stored documents in old
      * schema. You can save your documents by setting {@link
@@ -116,8 +116,6 @@
      * @see android.app.appsearch.AppSearchSchema.Migrator
      * @see android.app.appsearch.AppSearchMigrationHelper.Transformer
      */
-    // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
-    //  exposed.
     @NonNull
     ListenableFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest request);
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 82e967a..3cefe65 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -64,7 +64,7 @@
  * and which {@link JobServiceContext} to run each job on.
  */
 class JobConcurrencyManager {
-    private static final String TAG = JobSchedulerService.TAG;
+    private static final String TAG = JobSchedulerService.TAG + ".Concurrency";
     private static final boolean DEBUG = JobSchedulerService.DEBUG;
 
     static final String CONFIG_KEY_PREFIX_CONCURRENCY = "concurrency_";
@@ -321,13 +321,14 @@
         }
     }
 
+    /** Return {@code true} if the state was updated. */
     @GuardedBy("mLock")
-    private void refreshSystemStateLocked() {
+    private boolean refreshSystemStateLocked() {
         final long nowUptime = JobSchedulerService.sUptimeMillisClock.millis();
 
         // Only refresh the information every so often.
         if (nowUptime < mNextSystemStateRefreshTime) {
-            return;
+            return false;
         }
 
         final long start = mStatLogger.getTime();
@@ -340,11 +341,14 @@
         }
 
         mStatLogger.logDurationStat(Stats.REFRESH_SYSTEM_STATE, start);
+        return true;
     }
 
     @GuardedBy("mLock")
     private void updateCounterConfigLocked() {
-        refreshSystemStateLocked();
+        if (!refreshSystemStateLocked()) {
+            return;
+        }
 
         final WorkConfigLimitsPerMemoryTrimLevel workConfigs = mEffectiveInteractiveState
                 ? CONFIG_LIMITS_SCREEN_ON : CONFIG_LIMITS_SCREEN_OFF;
@@ -437,9 +441,10 @@
             // (sharing the same Uid as nextPending)
             int minPriorityForPreemption = Integer.MAX_VALUE;
             int selectedContextId = -1;
-            int workType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+            int allWorkTypes = getJobWorkTypes(nextPending);
+            int workType = mWorkCountTracker.canJobStart(allWorkTypes);
             boolean startingJob = false;
-            for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
+            for (int j = 0; j < MAX_JOB_CONTEXTS_COUNT; j++) {
                 JobStatus job = contextIdToJobMap[j];
                 int preferredUid = preferredUidForContext[j];
                 if (job == null) {
@@ -483,7 +488,7 @@
             if (startingJob) {
                 // Increase the counters when we're going to start a job.
                 workTypeForContext[selectedContextId] = workType;
-                mWorkCountTracker.stageJob(workType);
+                mWorkCountTracker.stageJob(workType, allWorkTypes);
             }
         }
         if (DEBUG) {
@@ -578,8 +583,10 @@
 
             JobStatus highestPriorityJob = null;
             int highPriWorkType = workType;
+            int highPriAllWorkTypes = workType;
             JobStatus backupJob = null;
             int backupWorkType = WORK_TYPE_NONE;
+            int backupAllWorkTypes = WORK_TYPE_NONE;
             for (int i = 0; i < pendingJobs.size(); i++) {
                 final JobStatus nextPending = pendingJobs.get(i);
 
@@ -589,11 +596,12 @@
 
                 if (worker.getPreferredUid() != nextPending.getUid()) {
                     if (backupJob == null) {
-                        int workAsType =
-                                mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+                        int allWorkTypes = getJobWorkTypes(nextPending);
+                        int workAsType = mWorkCountTracker.canJobStart(allWorkTypes);
                         if (workAsType != WORK_TYPE_NONE) {
                             backupJob = nextPending;
                             backupWorkType = workAsType;
+                            backupAllWorkTypes = allWorkTypes;
                         }
                     }
                     continue;
@@ -611,7 +619,8 @@
                 // reserved slots. We should just run the highest priority job we can find,
                 // though it would be ideal to use an available WorkType slot instead of
                 // overloading slots.
-                final int workAsType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+                highPriAllWorkTypes = getJobWorkTypes(nextPending);
+                final int workAsType = mWorkCountTracker.canJobStart(highPriAllWorkTypes);
                 if (workAsType == WORK_TYPE_NONE) {
                     // Just use the preempted job's work type since this new one is technically
                     // replacing it anyway.
@@ -624,7 +633,7 @@
                 if (DEBUG) {
                     Slog.d(TAG, "Running job " + jobStatus + " as preemption");
                 }
-                mWorkCountTracker.stageJob(highPriWorkType);
+                mWorkCountTracker.stageJob(highPriWorkType, highPriAllWorkTypes);
                 startJobLocked(worker, highestPriorityJob, highPriWorkType);
             } else {
                 if (DEBUG) {
@@ -635,7 +644,7 @@
                     if (DEBUG) {
                         Slog.d(TAG, "Running job " + jobStatus + " instead");
                     }
-                    mWorkCountTracker.stageJob(backupWorkType);
+                    mWorkCountTracker.stageJob(backupWorkType, backupAllWorkTypes);
                     startJobLocked(worker, backupJob, backupWorkType);
                 }
             }
@@ -647,6 +656,7 @@
             // find.
             JobStatus highestPriorityJob = null;
             int highPriWorkType = workType;
+            int highPriAllWorkTypes = workType;
             for (int i = 0; i < pendingJobs.size(); i++) {
                 final JobStatus nextPending = pendingJobs.get(i);
 
@@ -654,7 +664,8 @@
                     continue;
                 }
 
-                final int workAsType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+                final int allWorkTypes = getJobWorkTypes(nextPending);
+                final int workAsType = mWorkCountTracker.canJobStart(allWorkTypes);
                 if (workAsType == WORK_TYPE_NONE) {
                     continue;
                 }
@@ -663,6 +674,7 @@
                         < nextPending.lastEvaluatedPriority) {
                     highestPriorityJob = nextPending;
                     highPriWorkType = workAsType;
+                    highPriAllWorkTypes = allWorkTypes;
                 }
             }
 
@@ -672,7 +684,7 @@
                 if (DEBUG) {
                     Slog.d(TAG, "About to run job: " + jobStatus);
                 }
-                mWorkCountTracker.stageJob(highPriWorkType);
+                mWorkCountTracker.stageJob(highPriWorkType, highPriAllWorkTypes);
                 startJobLocked(worker, highestPriorityJob, highPriWorkType);
             }
         }
@@ -1102,26 +1114,58 @@
         }
 
         void incrementPendingJobCount(int workTypes) {
-            // We don't know which type we'll classify the job as when we run it yet, so make sure
-            // we have space in all applicable slots.
-            if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
-                mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + 1);
-            }
-            if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
-                mNumPendingJobs.put(WORK_TYPE_EJ, mNumPendingJobs.get(WORK_TYPE_EJ) + 1);
-            }
-            if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
-                mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1);
-            }
-            if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
-                mNumPendingJobs.put(WORK_TYPE_BGUSER, mNumPendingJobs.get(WORK_TYPE_BGUSER) + 1);
+            adjustPendingJobCount(workTypes, true);
+        }
+
+        void decrementPendingJobCount(int workTypes) {
+            if (adjustPendingJobCount(workTypes, false) > 1) {
+                // We don't need to adjust reservations if only one work type was modified
+                // because that work type is the one we're using.
+
+                // 0 is WORK_TYPE_NONE.
+                int workType = 1;
+                int rem = workTypes;
+                while (rem > 0) {
+                    if ((rem & 1) != 0) {
+                        maybeAdjustReservations(workType);
+                    }
+                    rem = rem >>> 1;
+                    workType = workType << 1;
+                }
             }
         }
 
-        void stageJob(@WorkType int workType) {
+        /** Returns the number of WorkTypes that were modified. */
+        private int adjustPendingJobCount(int workTypes, boolean add) {
+            final int adj = add ? 1 : -1;
+
+            int numAdj = 0;
+            // We don't know which type we'll classify the job as when we run it yet, so make sure
+            // we have space in all applicable slots.
+            if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
+                mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + adj);
+                numAdj++;
+            }
+            if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
+                mNumPendingJobs.put(WORK_TYPE_EJ, mNumPendingJobs.get(WORK_TYPE_EJ) + adj);
+                numAdj++;
+            }
+            if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
+                mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + adj);
+                numAdj++;
+            }
+            if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
+                mNumPendingJobs.put(WORK_TYPE_BGUSER, mNumPendingJobs.get(WORK_TYPE_BGUSER) + adj);
+                numAdj++;
+            }
+
+            return numAdj;
+        }
+
+        void stageJob(@WorkType int workType, int allWorkTypes) {
             final int newNumStartingJobs = mNumStartingJobs.get(workType) + 1;
             mNumStartingJobs.put(workType, newNumStartingJobs);
-            mNumPendingJobs.put(workType, Math.max(0, mNumPendingJobs.get(workType) - 1));
+            decrementPendingJobCount(allWorkTypes);
             if (newNumStartingJobs + mNumRunningJobs.get(workType)
                     > mNumActuallyReservedSlots.get(workType)) {
                 mNumUnspecializedRemaining--;
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 71fe55f..96f3bcc 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -3183,7 +3183,7 @@
                     TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
                     pw.println();
                     job.dump(pw, "    ", false, nowElapsed);
-                    int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
+                    int priority = evaluateJobPriorityLocked(job);
                     pw.print("    Evaluated priority: ");
                     pw.println(JobInfo.getPriorityString(priority));
 
@@ -3349,7 +3349,7 @@
                     job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
 
                     proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY,
-                            evaluateJobPriorityLocked(jsc.getRunningJobLocked()));
+                            evaluateJobPriorityLocked(job));
 
                     proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
                             nowUptime - job.madeActive);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index d15bae0..da6f9fe 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -20,6 +20,7 @@
 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
+import android.annotation.Nullable;
 import android.app.job.IJobCallback;
 import android.app.job.IJobService;
 import android.app.job.JobInfo;
@@ -326,6 +327,7 @@
     /**
      * Used externally to query the running job. Will return null if there is no job running.
      */
+    @Nullable
     JobStatus getRunningJobLocked() {
         return mRunningJob;
     }
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
index 67fa9bb..a2366df 100644
--- a/apex/media/framework/api/current.txt
+++ b/apex/media/framework/api/current.txt
@@ -8,9 +8,10 @@
     method @NonNull public java.util.List<java.lang.String> getSupportedVideoMimeTypes();
     method @NonNull public java.util.List<java.lang.String> getUnsupportedHdrTypes();
     method @NonNull public java.util.List<java.lang.String> getUnsupportedVideoMimeTypes();
-    method public boolean isHdrTypeSupported(@NonNull String) throws android.media.ApplicationMediaCapabilities.FormatNotFoundException;
+    method public boolean isFormatSpecified(@NonNull String);
+    method public boolean isHdrTypeSupported(@NonNull String);
     method public boolean isSlowMotionSupported();
-    method public boolean isVideoMimeTypeSupported(@NonNull String) throws android.media.ApplicationMediaCapabilities.FormatNotFoundException;
+    method public boolean isVideoMimeTypeSupported(@NonNull String);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.ApplicationMediaCapabilities> CREATOR;
   }
@@ -24,10 +25,6 @@
     method @NonNull public android.media.ApplicationMediaCapabilities build();
   }
 
-  public static class ApplicationMediaCapabilities.FormatNotFoundException extends android.util.AndroidException {
-    ctor public ApplicationMediaCapabilities.FormatNotFoundException(@NonNull String);
-  }
-
   public class MediaCommunicationManager {
     method @IntRange(from=1) public int getVersion();
   }
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index aefeab6..685cf0d 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -22,7 +22,6 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.AndroidException;
 import android.util.Log;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -79,17 +78,7 @@
 public final class ApplicationMediaCapabilities implements Parcelable {
     private static final String TAG = "ApplicationMediaCapabilities";
 
-    /**
-     * This exception is thrown when a given format is not specified in the media capabilities.
-     */
-    public static class FormatNotFoundException extends AndroidException {
-        public FormatNotFoundException(@NonNull String format) {
-            super(format);
-        }
-    }
-
     /** List of supported video codec mime types. */
-    // TODO: init it with avc and mpeg4 as application is assuming to support them.
     private Set<String> mSupportedVideoMimeTypes = new HashSet<>();
 
     /** List of unsupported video codec mime types. */
@@ -113,39 +102,54 @@
 
     /**
      * Query if a video codec format is supported by the application.
+     * <p>
+     * If the application has not specified supporting the format or not, this will return false.
+     * Use {@link #isFormatSpecified(String)} to query if a format is specified or not.
+     *
      * @param videoMime The mime type of the video codec format. Must be the one used in
      * {@link MediaFormat#KEY_MIME}.
      * @return true if application supports the video codec format, false otherwise.
-     * @throws FormatNotFoundException if the application did not specify the codec either in the
-     * supported or unsupported formats.
      */
     public boolean isVideoMimeTypeSupported(
-            @NonNull String videoMime) throws FormatNotFoundException {
-        if (mUnsupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
-            return false;
-        } else if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
+            @NonNull String videoMime) {
+        if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
             return true;
-        } else {
-            throw new FormatNotFoundException(videoMime);
         }
+        return false;
     }
 
     /**
      * Query if a HDR type is supported by the application.
+     * <p>
+     * If the application has not specified supporting the format or not, this will return false.
+     * Use {@link #isFormatSpecified(String)} to query if a format is specified or not.
+     *
      * @param hdrType The type of the HDR format.
      * @return true if application supports the HDR format, false otherwise.
-     * @throws FormatNotFoundException if the application did not specify the format either in the
-     * supported or unsupported formats.
      */
     public boolean isHdrTypeSupported(
-            @NonNull @MediaFeature.MediaHdrType String hdrType) throws FormatNotFoundException {
-        if (mUnsupportedHdrTypes.contains(hdrType)) {
-            return false;
-        } else if (mSupportedHdrTypes.contains(hdrType)) {
+            @NonNull @MediaFeature.MediaHdrType String hdrType) {
+        if (mSupportedHdrTypes.contains(hdrType)) {
             return true;
-        } else {
-            throw new FormatNotFoundException(hdrType);
         }
+        return false;
+    }
+
+    /**
+     * Query if a format is specified by the application.
+     * <p>
+     * The format could be either the video format or the hdr format.
+     *
+     * @param format The name of the format.
+     * @return true if application specifies the format, false otherwise.
+     */
+    public boolean isFormatSpecified(@NonNull String format) {
+        if (mSupportedVideoMimeTypes.contains(format) || mUnsupportedVideoMimeTypes.contains(format)
+                || mSupportedHdrTypes.contains(format) || mUnsupportedHdrTypes.contains(format)) {
+            return true;
+
+        }
+        return false;
     }
 
     @Override
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index ce7726a..c924d9a 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -1062,14 +1062,8 @@
                             "Source video format hint must be set!");
                 }
 
-                boolean supportHevc = false;
-                try {
-                    supportHevc = mClientCaps.isVideoMimeTypeSupported(
-                            MediaFormat.MIMETYPE_VIDEO_HEVC);
-                } catch (ApplicationMediaCapabilities.FormatNotFoundException ex) {
-                    // Set to false if application did not specify.
-                    supportHevc = false;
-                }
+                boolean supportHevc = mClientCaps.isVideoMimeTypeSupported(
+                        MediaFormat.MIMETYPE_VIDEO_HEVC);
                 if (!supportHevc && MediaFormat.MIMETYPE_VIDEO_HEVC.equals(
                         mSrcVideoFormatHint.getString(MediaFormat.KEY_MIME))) {
                     return true;
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index ed717c4..4b7eda0 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.UserIdInt;
 import android.app.backup.BackupManager;
+import android.app.backup.BackupManager.OperationType;
 import android.app.backup.BackupManagerMonitor;
 import android.app.backup.BackupProgress;
 import android.app.backup.BackupTransport;
@@ -666,7 +667,7 @@
 
         // The rest of the 'list' options work with a restore session on the current transport
         try {
-            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
+            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP);
             if (mRestore == null) {
                 System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
                 return;
@@ -821,7 +822,7 @@
 
         try {
             boolean didRestore = false;
-            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
+            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP);
             if (mRestore == null) {
                 System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
                 return;
diff --git a/core/api/current.txt b/core/api/current.txt
index 2531d81..dd57b6a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5587,10 +5587,10 @@
     field public static final String EXTRA_PROGRESS = "android.progress";
     field public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
     field public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
-    field public static final String EXTRA_PROMOTE_PICTURE = "android.promotePicture";
     field public static final String EXTRA_REMOTE_INPUT_DRAFT = "android.remoteInputDraft";
     field public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
     field @Deprecated public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+    field public static final String EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED = "android.showBigPictureWhenCollapsed";
     field public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
     field public static final String EXTRA_SHOW_WHEN = "android.showWhen";
     field @Deprecated public static final String EXTRA_SMALL_ICON = "android.icon";
@@ -6896,6 +6896,7 @@
     method public void onLockTaskModeEntering(@NonNull android.content.Context, @NonNull android.content.Intent, @NonNull String);
     method public void onLockTaskModeExiting(@NonNull android.content.Context, @NonNull android.content.Intent);
     method public void onNetworkLogsAvailable(@NonNull android.content.Context, @NonNull android.content.Intent, long, @IntRange(from=1) int);
+    method public void onOperationSafetyStateChanged(@NonNull android.content.Context, int, boolean);
     method @Deprecated public void onPasswordChanged(@NonNull android.content.Context, @NonNull android.content.Intent);
     method public void onPasswordChanged(@NonNull android.content.Context, @NonNull android.content.Intent, @NonNull android.os.UserHandle);
     method @Deprecated public void onPasswordExpiring(@NonNull android.content.Context, @NonNull android.content.Intent);
@@ -7072,6 +7073,7 @@
     method public boolean isProfileOwnerApp(String);
     method public boolean isProvisioningAllowed(@NonNull String);
     method public boolean isResetPasswordTokenActive(android.content.ComponentName);
+    method public boolean isSafeOperation(int);
     method public boolean isSecurityLoggingEnabled(@Nullable android.content.ComponentName);
     method public boolean isUninstallBlocked(@Nullable android.content.ComponentName, String);
     method public boolean isUniqueDeviceAttestationSupported();
@@ -7244,7 +7246,7 @@
     field public static final String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
     field public static final String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
     field public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI";
-    field public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
+    field @Deprecated public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
     field public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE";
     field public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT = "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT";
     field public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER";
@@ -7300,6 +7302,7 @@
     field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
     field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
     field public static final String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
+    field public static final int OPERATION_SAFETY_REASON_DRIVING_DISTRACTION = 1; // 0x1
     field public static final int PASSWORD_COMPLEXITY_HIGH = 327680; // 0x50000
     field public static final int PASSWORD_COMPLEXITY_LOW = 65536; // 0x10000
     field public static final int PASSWORD_COMPLEXITY_MEDIUM = 196608; // 0x30000
@@ -7336,7 +7339,6 @@
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
-    field public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1; // 0x1
     field public static final int WIPE_EUICC = 4; // 0x4
     field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -7490,7 +7492,7 @@
 
   public final class UnsafeStateException extends java.lang.IllegalStateException implements android.os.Parcelable {
     method public int describeContents();
-    method public int getReason();
+    method @NonNull public java.util.List<java.lang.Integer> getReasons();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UnsafeStateException> CREATOR;
   }
@@ -38074,6 +38076,10 @@
     method public final void setNotificationsShown(String[]);
     method public final void snoozeNotification(String, long);
     method public final void updateNotificationChannel(@NonNull String, @NonNull android.os.UserHandle, @NonNull android.app.NotificationChannel);
+    field public static final int FLAG_FILTER_TYPE_ALERTING = 2; // 0x2
+    field public static final int FLAG_FILTER_TYPE_CONVERSATIONS = 1; // 0x1
+    field public static final int FLAG_FILTER_TYPE_ONGOING = 8; // 0x8
+    field public static final int FLAG_FILTER_TYPE_SILENT = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
     field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
@@ -38082,6 +38088,7 @@
     field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
     field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
     field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
+    field public static final String META_DATA_DEFAULT_FILTER_TYPES = "android.service.notification.default_filter_types";
     field public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1; // 0x1
     field public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3; // 0x3
     field public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2; // 0x2
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fb6cfea..d7dc6ef 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -905,8 +905,8 @@
     field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
     field public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
     field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
-    field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
-    field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
+    field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
+    field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
     field public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME";
     field public static final String EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE = "android.app.extra.PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE";
     field public static final String EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER = "android.app.extra.PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER";
@@ -2523,7 +2523,8 @@
     field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
     field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
     field public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur";
-    field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
+    field @Deprecated public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
+    field public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION = "android.software.incremental_delivery_version";
     field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
     field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
     field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
@@ -11949,6 +11950,7 @@
     field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
     field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
     field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
+    field public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED = "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
     field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 909c8e4..694507d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -391,11 +391,11 @@
     method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
     method public boolean isCurrentInputMethodSetByOwner();
     method public boolean isFactoryResetProtectionPolicySupported();
+    method @NonNull public static String operationSafetyReasonToString(int);
     method @NonNull public static String operationToString(int);
     method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
     method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
     method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
-    method @NonNull public static String unsafeOperationReasonToString(int);
     field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
     field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
     field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
@@ -425,6 +425,7 @@
     field public static final int OPERATION_REMOVE_KEY_PAIR = 28; // 0x1c
     field public static final int OPERATION_REMOVE_USER = 6; // 0x6
     field public static final int OPERATION_REQUEST_BUGREPORT = 29; // 0x1d
+    field public static final int OPERATION_SAFETY_REASON_NONE = -1; // 0xffffffff
     field public static final int OPERATION_SET_ALWAYS_ON_VPN_PACKAGE = 30; // 0x1e
     field public static final int OPERATION_SET_APPLICATION_HIDDEN = 15; // 0xf
     field public static final int OPERATION_SET_APPLICATION_RESTRICTIONS = 16; // 0x10
@@ -460,7 +461,6 @@
     field public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
     field public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7; // 0x7
     field public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5; // 0x5
-    field public static final int UNSAFE_OPERATION_REASON_NONE = -1; // 0xffffffff
   }
 
   public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
diff --git a/core/java/android/accounts/OWNERS b/core/java/android/accounts/OWNERS
index ea5fd36..8dcc04a 100644
--- a/core/java/android/accounts/OWNERS
+++ b/core/java/android/accounts/OWNERS
@@ -3,7 +3,6 @@
 sandrakwan@google.com
 hackbod@google.com
 svetoslavganov@google.com
-moltmann@google.com
 fkupolov@google.com
 yamasani@google.com
 omakoto@google.com
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 050f34a..b6fc47f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1208,7 +1208,8 @@
      * of a {@link BigPictureStyle} notification.  This will replace a
      * {@link Builder#setLargeIcon(Icon) large icon} in that state if one was provided.
      */
-    public static final String EXTRA_PROMOTE_PICTURE = "android.promotePicture";
+    public static final String EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED =
+            "android.showBigPictureWhenCollapsed";
 
     /**
      * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
@@ -7059,7 +7060,7 @@
         private Icon mBigLargeIcon;
         private boolean mBigLargeIconSet = false;
         private CharSequence mPictureContentDescription;
-        private boolean mPromotePicture;
+        private boolean mShowBigPictureWhenCollapsed;
 
         public BigPictureStyle() {
         }
@@ -7124,7 +7125,7 @@
          */
         @NonNull
         public BigPictureStyle showBigPictureWhenCollapsed(boolean show) {
-            mPromotePicture = show;
+            mShowBigPictureWhenCollapsed = show;
             return this;
         }
 
@@ -7195,7 +7196,7 @@
          */
         @Override
         public RemoteViews makeContentView(boolean increasedHeight) {
-            if (mPicture == null || !mPromotePicture) {
+            if (mPicture == null || !mShowBigPictureWhenCollapsed) {
                 return super.makeContentView(increasedHeight);
             }
 
@@ -7225,7 +7226,7 @@
          */
         @Override
         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
-            if (mPicture == null || !mPromotePicture) {
+            if (mPicture == null || !mShowBigPictureWhenCollapsed) {
                 return super.makeHeadsUpContentView(increasedHeight);
             }
 
@@ -7309,7 +7310,7 @@
                 extras.putCharSequence(EXTRA_PICTURE_CONTENT_DESCRIPTION,
                         mPictureContentDescription);
             }
-            extras.putBoolean(EXTRA_PROMOTE_PICTURE, mPromotePicture);
+            extras.putBoolean(EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED, mShowBigPictureWhenCollapsed);
             extras.putParcelable(EXTRA_PICTURE, mPicture);
         }
 
@@ -7330,7 +7331,7 @@
                         extras.getCharSequence(EXTRA_PICTURE_CONTENT_DESCRIPTION);
             }
 
-            mPromotePicture = extras.getBoolean(EXTRA_PROMOTE_PICTURE);
+            mShowBigPictureWhenCollapsed = extras.getBoolean(EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED);
             mPicture = extras.getParcelable(EXTRA_PICTURE);
         }
 
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index d175a66..4dbff0c 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -16,6 +16,8 @@
 
 package android.app.admin;
 
+import static android.app.admin.DevicePolicyManager.OperationSafetyReason;
+
 import android.accounts.AccountManager;
 import android.annotation.BroadcastBehavior;
 import android.annotation.IntDef;
@@ -35,6 +37,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.security.KeyChain;
+import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -72,8 +75,8 @@
  * </div>
  */
 public class DeviceAdminReceiver extends BroadcastReceiver {
-    private static String TAG = "DevicePolicy";
-    private static boolean localLOGV = false;
+    private static final String TAG = "DevicePolicy";
+    private static final boolean LOCAL_LOGV = false;
 
     /**
      * This is the primary action that a device administrator must implement to be
@@ -509,6 +512,36 @@
     public static final String EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE =
             "android.app.extra.TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE";
 
+    /**
+     * Broadcast action: notify the admin that the state of operations that can be unsafe because
+     * of a given reason (specified by the {@link #EXTRA_OPERATION_SAFETY_REASON} {@code int} extra)
+     * has changed (the new value is specified by the {@link #EXTRA_OPERATION_SAFETY_STATE}
+     * {@code boolean} extra).
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_OPERATION_SAFETY_STATE_CHANGED =
+            "android.app.action.OPERATION_SAFETY_STATE_CHANGED";
+
+    /**
+     * An {@code int} extra specifying an {@link OperationSafetyReason}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_OPERATION_SAFETY_REASON  =
+            "android.app.extra.OPERATION_SAFETY_REASON";
+
+    /**
+     * An {@code boolean} extra specifying whether an operation will fail due to a
+     * {@link OperationSafetyReason}. {@code true} means operations that rely on that reason are
+     * safe, while {@code false} means they're unsafe.
+     *
+     * @hide
+     */
+    public static final String EXTRA_OPERATION_SAFETY_STATE  =
+            "android.app.extra.OPERATION_SAFETY_STATE";
+
     private DevicePolicyManager mManager;
     private ComponentName mWho;
 
@@ -1018,6 +1051,51 @@
     }
 
     /**
+     * Called to notify the state of operations that can be unsafe to execute has changed.
+     *
+     * <p><b>Note:/b> notice that the operation safety state might change between the time this
+     * callback is received and the operation's method on {@link DevicePolicyManager} is called, so
+     * calls to the latter could still throw a {@link UnsafeStateException} even when this method
+     * is called with {@code isSafe} as {@code true}
+     *
+     * @param context the running context as per {@link #onReceive}
+     * @param reason the reason an operation could be unsafe.
+     * @param isSafe whether the operation is safe to be executed.
+     */
+    public void onOperationSafetyStateChanged(@NonNull Context context,
+            @OperationSafetyReason int reason, boolean isSafe) {
+        if (LOCAL_LOGV) {
+            Log.v(TAG, String.format("onOperationSafetyStateChanged(): %s=%b",
+                    DevicePolicyManager.operationSafetyReasonToString(reason), isSafe));
+        }
+    }
+
+    private void onOperationSafetyStateChanged(Context context, Intent intent) {
+        if (!hasRequiredExtra(intent, EXTRA_OPERATION_SAFETY_REASON)
+                || !hasRequiredExtra(intent, EXTRA_OPERATION_SAFETY_STATE)) {
+            return;
+        }
+
+        int reason = intent.getIntExtra(EXTRA_OPERATION_SAFETY_REASON,
+                DevicePolicyManager.OPERATION_SAFETY_REASON_NONE);
+        if (!DevicePolicyManager.isValidOperationSafetyReason(reason)) {
+            Log.wtf(TAG, "Received invalid reason on " + intent.getAction() + ": " + reason);
+            return;
+        }
+        boolean isSafe = intent.getBooleanExtra(EXTRA_OPERATION_SAFETY_STATE,
+                /* defaultValue=*/ false);
+
+        onOperationSafetyStateChanged(context, reason, isSafe);
+    }
+
+    private boolean hasRequiredExtra(Intent intent, String extra) {
+        if (intent.hasExtra(extra)) return true;
+
+        Log.wtf(TAG, "Missing '" + extra + "' on intent " +  intent);
+        return false;
+    }
+
+    /**
      * Intercept standard device administrator broadcasts.  Implementations
      * should not override this method; it is better to implement the
      * convenience callbacks for each action.
@@ -1025,6 +1103,9 @@
     @Override
     public void onReceive(@NonNull Context context, @NonNull Intent intent) {
         String action = intent.getAction();
+        if (LOCAL_LOGV) {
+            Log.v(TAG, "onReceive(): received " + action + " on user " + context.getUserId());
+        }
 
         if (ACTION_PASSWORD_CHANGED.equals(action)) {
             onPasswordChanged(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
@@ -1092,6 +1173,8 @@
         } else if (ACTION_AFFILIATED_PROFILE_TRANSFER_OWNERSHIP_COMPLETE.equals(action)) {
             onTransferAffiliatedProfileOwnershipComplete(context,
                     intent.getParcelableExtra(Intent.EXTRA_USER));
+        } else if (ACTION_OPERATION_SAFETY_STATE_CHANGED.equals(action)) {
+            onOperationSafetyStateChanged(context, intent);
         }
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ff41d1c8..ff6f6a0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -206,7 +206,6 @@
      * {@link android.os.Build.VERSION_CODES#N}</li>
      * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_SKIP_USER_CONSENT}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}, optional</li>
@@ -250,7 +249,6 @@
      * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
      * </ul>
      *
      * <p>If provisioning fails, the device returns to its previous state.
@@ -289,7 +287,6 @@
      * <li>{@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS}, optional</li>
      * </ul>
@@ -388,8 +385,6 @@
      * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_LOCAL_TIME} (convert to String), optional</li>
      * <li>{@link #EXTRA_PROVISIONING_TIME_ZONE}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_LOCALE}, optional</li>
@@ -438,8 +433,6 @@
      * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_SUPPORT_URL}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_ORGANIZATION_NAME}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
@@ -654,7 +647,10 @@
      *
      * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE} or
      * {@link #ACTION_PROVISION_MANAGED_DEVICE}.
+     *
+     * @deprecated Color customization is no longer supported in the provisioning flow.
      */
+    @Deprecated
     public static final String EXTRA_PROVISIONING_MAIN_COLOR =
              "android.app.extra.PROVISIONING_MAIN_COLOR";
 
@@ -905,8 +901,10 @@
      * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
      * or {@link #ACTION_PROVISION_FINANCED_DEVICE}
      *
+     * @deprecated This extra is no longer respected in the provisioning flow.
      * @hide
      */
+    @Deprecated
     @SystemApi
     public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL =
             "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
@@ -930,9 +928,11 @@
      * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
      * or {@link #ACTION_PROVISION_FINANCED_DEVICE}
      *
+     * @deprecated This extra is no longer respected in the provisioning flow.
      * @hide
      */
     @SystemApi
+    @Deprecated
     public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI =
             "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
 
@@ -2922,33 +2922,61 @@
         return DebugUtils.constantToString(DevicePolicyManager.class, PREFIX_OPERATION, operation);
     }
 
-    private static final String PREFIX_UNSAFE_OPERATION_REASON = "UNSAFE_OPERATION_REASON_";
+    private static final String PREFIX_OPERATION_SAFETY_REASON = "OPERATION_SAFETY_REASON_";
 
     /** @hide */
-    @IntDef(prefix = PREFIX_UNSAFE_OPERATION_REASON, value = {
-            UNSAFE_OPERATION_REASON_NONE,
-            UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION
+    @IntDef(prefix = PREFIX_OPERATION_SAFETY_REASON, value = {
+            OPERATION_SAFETY_REASON_NONE,
+            OPERATION_SAFETY_REASON_DRIVING_DISTRACTION
     })
     @Retention(RetentionPolicy.SOURCE)
-    public static @interface UnsafeOperationReason {
+    public static @interface OperationSafetyReason {
     }
 
     /** @hide */
     @TestApi
-    public static final int UNSAFE_OPERATION_REASON_NONE = -1;
+    public static final int OPERATION_SAFETY_REASON_NONE = -1;
 
     /**
      * Indicates that a {@link UnsafeStateException} was thrown because the operation would distract
      * the driver of the vehicle.
      */
-    public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1;
+    public static final int OPERATION_SAFETY_REASON_DRIVING_DISTRACTION = 1;
 
     /** @hide */
     @NonNull
     @TestApi
-    public static String unsafeOperationReasonToString(@UnsafeOperationReason int reason) {
+    public static String operationSafetyReasonToString(@OperationSafetyReason int reason) {
         return DebugUtils.constantToString(DevicePolicyManager.class,
-                PREFIX_UNSAFE_OPERATION_REASON, reason);
+                PREFIX_OPERATION_SAFETY_REASON, reason);
+    }
+
+    /** @hide */
+    public static boolean isValidOperationSafetyReason(@OperationSafetyReason int reason) {
+        return reason == OPERATION_SAFETY_REASON_DRIVING_DISTRACTION;
+    }
+
+    /**
+     * Checks if it's safe to run operations that can be affected by the given {@code reason}.
+     *
+     * <p><b>Note:/b> notice that the operation safety state might change between the time this
+     * method returns and the operation's method is called, so calls to the latter could still throw
+     * a {@link UnsafeStateException} even when this method returns {@code true}.
+     *
+     * @param reason currently, only supported reason is
+     * {@link #OPERATION_SAFETY_REASON_DRIVING_DISTRACTION}.
+     *
+     * @return whether it's safe to run operations that can be affected by the given {@code reason}.
+     */
+    // TODO(b/173541467): should it throw SecurityException if caller is not admin?
+    public boolean isSafeOperation(@OperationSafetyReason int reason) {
+        if (mService == null) return false;
+
+        try {
+            return mService.isSafeOperation(reason);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /** @hide */
@@ -13157,7 +13185,7 @@
     @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS)
     public void setNextOperationSafety(@DevicePolicyOperation int operation,
-            @UnsafeOperationReason int reason) {
+            @OperationSafetyReason int reason) {
         if (mService != null) {
             try {
                 mService.setNextOperationSafety(operation, reason);
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index a0d2977..67f5c36 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -18,6 +18,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.UserHandle;
@@ -255,4 +256,14 @@
      * {@link #supportsResetOp(int)} is true.
      */
     public abstract void resetOp(int op, String packageName, @UserIdInt int userId);
+
+    /**
+     * Notifies the system that an unsafe operation reason has changed.
+     *
+     * @throws IllegalArgumentException if {@code checker} is not the same as set on
+     *         {@code DevicePolicyManagerService}.
+     */
+    public abstract void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker,
+            @OperationSafetyReason int reason, boolean isSafe);
+
 }
diff --git a/core/java/android/app/admin/DevicePolicySafetyChecker.java b/core/java/android/app/admin/DevicePolicySafetyChecker.java
index 6c6f2aa..17b74b1 100644
--- a/core/java/android/app/admin/DevicePolicySafetyChecker.java
+++ b/core/java/android/app/admin/DevicePolicySafetyChecker.java
@@ -17,7 +17,7 @@
 
 import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
-import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -31,15 +31,20 @@
     /**
      * Returns whether the given {@code operation} can be safely executed at the moment.
      */
-    @UnsafeOperationReason
+    @OperationSafetyReason
     int getUnsafeOperationReason(@DevicePolicyOperation int operation);
 
     /**
+     * Return whether it's safe to run operations that can be affected by the given {@code reason}.
+     */
+    boolean isSafeOperation(@OperationSafetyReason int reason);
+
+    /**
      * Returns a new exception for when the given {@code operation} cannot be safely executed.
      */
     @NonNull
     default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation,
-            @UnsafeOperationReason int reason) {
+            @OperationSafetyReason int reason) {
         return new UnsafeStateException(operation, reason);
     }
 
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 89f30cc..032cf24 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -492,6 +492,7 @@
     boolean canProfileOwnerResetPasswordWhenLocked(int userId);
 
     void setNextOperationSafety(int operation, int reason);
+    boolean isSafeOperation(int reason);
 
     String getEnrollmentSpecificId(String callerPackage);
     void setOrganizationIdForUser(in String callerPackage, in String enterpriseId, int userId);
diff --git a/core/java/android/app/admin/UnsafeStateException.java b/core/java/android/app/admin/UnsafeStateException.java
index 56eeb06..f1f6526 100644
--- a/core/java/android/app/admin/UnsafeStateException.java
+++ b/core/java/android/app/admin/UnsafeStateException.java
@@ -15,18 +15,20 @@
  */
 package android.app.admin;
 
-import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION;
-import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
+import static android.app.admin.DevicePolicyManager.isValidOperationSafetyReason;
 
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
-import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.util.Preconditions;
 
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * Exception thrown when a {@link android.app.admin.DevicePolicyManager} operation failed because it
  * was not safe to be executed at that moment.
@@ -39,17 +41,15 @@
 public final class UnsafeStateException extends IllegalStateException implements Parcelable {
 
     private final @DevicePolicyOperation int mOperation;
-    private final @UnsafeOperationReason int mReason;
+    private final @OperationSafetyReason int mReason;
 
     /** @hide */
     @TestApi
     public UnsafeStateException(@DevicePolicyOperation int operation,
-            @UnsafeOperationReason int reason) {
+            @OperationSafetyReason int reason) {
         super();
-        Preconditions.checkArgument(reason == UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
-                "invalid reason %d, must be %d (%s)", reason,
-                UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
-                unsafeOperationReasonToString(UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION));
+        Preconditions.checkArgument(isValidOperationSafetyReason(reason), "invalid reason %d",
+                reason);
         mOperation = operation;
         mReason = reason;
     }
@@ -61,19 +61,20 @@
     }
 
     /**
-     * Gets the reason the operation is unsafe.
+     * Gets the reasons the operation is unsafe.
      *
      * @return currently, only valid reason is
-     * {@link android.app.admin.DevicePolicyManager#UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION}.
+     * {@link android.app.admin.DevicePolicyManager#OPERATION_SAFETY_REASON_DRIVING_DISTRACTION}.
      */
-    public @UnsafeOperationReason int getReason() {
-        return mReason;
+    @NonNull
+    public List<Integer> getReasons() {
+        return Arrays.asList(mReason);
     }
 
     /** @hide */
     @Override
     public String getMessage() {
-        return DevicePolicyManager.unsafeOperationReasonToString(mReason);
+        return DevicePolicyManager.operationSafetyReasonToString(mReason);
     }
 
     @Override
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index dae565e..673de8f 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -361,7 +361,36 @@
             try {
                 // All packages, current transport
                 IRestoreSession binder =
-                        sService.beginRestoreSessionForUser(mContext.getUserId(), null, null);
+                        sService.beginRestoreSessionForUser(mContext.getUserId(), null, null,
+                                OperationType.BACKUP);
+                if (binder != null) {
+                    session = new RestoreSession(mContext, binder);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "beginRestoreSession() couldn't connect");
+            }
+        }
+        return session;
+    }
+
+    /**
+     * Begin the process of restoring data from backup.  See the
+     * {@link android.app.backup.RestoreSession} class for documentation on that process.
+     *
+     * @param operationType Type of the operation, see {@link OperationType}
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public RestoreSession beginRestoreSession(@OperationType int operationType) {
+        RestoreSession session = null;
+        checkServiceBinder();
+        if (sService != null) {
+            try {
+                // All packages, current transport
+                IRestoreSession binder =
+                        sService.beginRestoreSessionForUser(mContext.getUserId(), null, null,
+                                operationType);
                 if (binder != null) {
                     session = new RestoreSession(mContext, binder);
                 }
@@ -772,7 +801,7 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.BACKUP)
     public int requestBackup(String[] packages, BackupObserver observer) {
-        return requestBackup(packages, observer, null, 0);
+        return requestBackup(packages, observer, null, 0, OperationType.BACKUP);
     }
 
     /**
@@ -797,6 +826,31 @@
     @RequiresPermission(android.Manifest.permission.BACKUP)
     public int requestBackup(String[] packages, BackupObserver observer,
             BackupManagerMonitor monitor, int flags) {
+        return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP);
+    }
+
+    /**
+     * Request an immediate backup, providing an observer to which results of the backup operation
+     * will be published. The Android backup system will decide for each package whether it will
+     * be full app data backup or key/value-pair-based backup.
+     *
+     * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all
+     * provided packages using the remote transport.
+     *
+     * @param packages List of package names to backup.
+     * @param observer The {@link BackupObserver} to receive callbacks during the backup
+     *                 operation. Could be {@code null}.
+     * @param monitor  The {@link BackupManagerMonitorWrapper} to receive callbacks of important
+     *                 events during the backup operation. Could be {@code null}.
+     * @param flags    {@link #FLAG_NON_INCREMENTAL_BACKUP}.
+     * @param operationType {@link OperationType}
+     * @return {@link BackupManager#SUCCESS} on success; nonzero on error.
+     * @throws IllegalArgumentException on null or empty {@code packages} param.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public int requestBackup(String[] packages, BackupObserver observer,
+            BackupManagerMonitor monitor, int flags, @OperationType int operationType) {
         checkServiceBinder();
         if (sService != null) {
             try {
@@ -806,7 +860,8 @@
                 BackupManagerMonitorWrapper monitorWrapper = monitor == null
                         ? null
                         : new BackupManagerMonitorWrapper(monitor);
-                return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags);
+                return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags,
+                        operationType);
             } catch (RemoteException e) {
                 Log.e(TAG, "requestBackup() couldn't connect");
             }
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index bf5be95..e1bbc08 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -547,9 +547,11 @@
      *        set can be restored.
      * @param transportID The name of the transport to use for the restore operation.
      *        May be null, in which case the current active transport is used.
+     * @param operationType Type of the operation, see {@link BackupManager#OperationType}
      * @return An interface to the restore session, or null on error.
      */
-    IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID);
+    IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID,
+            int operationType);
 
     /**
      * Notify the backup manager that a BackupAgent has completed the operation
@@ -678,7 +680,7 @@
      * {@link android.app.backup.IBackupManager.requestBackupForUser} for the calling user id.
      */
     int requestBackup(in String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor,
-            int flags);
+        int flags, int operationType);
 
     /**
      * Cancel all running backups. After this call returns, no currently running backups will
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6cfcce3..a3c3500 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3592,9 +3592,12 @@
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
      * the requisite kernel support to support incremental delivery aka Incremental FileSystem.
      *
-     * @see IncrementalManager#isEnabled
+     * @see IncrementalManager#isFeatureEnabled
      * @hide
+     *
+     * @deprecated Use {@link #FEATURE_INCREMENTAL_DELIVERY_VERSION} instead.
      */
+    @Deprecated
     @SystemApi
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_INCREMENTAL_DELIVERY =
@@ -3602,6 +3605,20 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * feature not present - IncFs is not present on the device.
+     * 1 - IncFs v1, core features, no PerUid support. Optional in R.
+     * 2 - IncFs v2, PerUid support, fs-verity support. Required in S.
+     *
+     * @see IncrementalManager#isFeatureEnabled and IncrementalManager#isV2()
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION =
+            "android.software.incremental_delivery_version";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
      * The device has tuner hardware to support tuner operations.
      *
      * <p>This feature implies that the device has the tuner HAL implementation.
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index a2e533a..0e70a3e 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -28,6 +28,9 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Set;
@@ -477,6 +480,8 @@
      */
     public @Nullable CharSequence nonLocalizedDescription;
 
+    private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
+
     /**
      * A {@link Set} of trusted signing certificate digests. If this permission has the {@link
      * #PROTECTION_FLAG_KNOWN_SIGNER} flag set the permission will be granted to a requesting app
@@ -688,6 +693,7 @@
         dest.writeInt(descriptionRes);
         dest.writeInt(requestRes);
         TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
+        sForStringSet.parcel(knownCerts, dest, parcelableFlags);
     }
 
     /** @hide */
@@ -753,5 +759,6 @@
         descriptionRes = source.readInt();
         requestRes = source.readInt();
         nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        knownCerts = sForStringSet.unparcel(source);
     }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java
index 35bb33c..37e0e87 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermission.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java
@@ -21,16 +21,22 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
 
+import java.util.Locale;
 import java.util.Set;
 
 /** @hide */
 public class ParsedPermission extends ParsedComponent {
 
+    private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
+
     @Nullable
     String backgroundPermission;
     @Nullable
@@ -89,6 +95,19 @@
         return knownCerts;
     }
 
+    protected void setKnownCert(String knownCert) {
+        // Convert the provided digest to upper case for consistent Set membership
+        // checks when verifying the signing certificate digests of requesting apps.
+        this.knownCerts = Set.of(knownCert.toUpperCase(Locale.US));
+    }
+
+    protected void setKnownCerts(String[] knownCerts) {
+        this.knownCerts = new ArraySet<>();
+        for (String knownCert : knownCerts) {
+            this.knownCerts.add(knownCert.toUpperCase(Locale.US));
+        }
+    }
+
     public int calculateFootprint() {
         int size = getName().length();
         if (getNonLocalizedLabel() != null) {
@@ -117,6 +136,7 @@
         dest.writeInt(this.protectionLevel);
         dest.writeBoolean(this.tree);
         dest.writeParcelable(this.parsedPermissionGroup, flags);
+        sForStringSet.parcel(knownCerts, dest, flags);
     }
 
     protected ParsedPermission(Parcel in) {
@@ -129,6 +149,7 @@
         this.protectionLevel = in.readInt();
         this.tree = in.readBoolean();
         this.parsedPermissionGroup = in.readParcelable(boot);
+        this.knownCerts = sForStringSet.unparcel(in);
     }
 
     public static final Parcelable.Creator<ParsedPermission> CREATOR =
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index a7cecbe..8afa70e 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -25,7 +25,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -33,8 +32,6 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
-import java.util.Locale;
-import java.util.Set;
 
 /** @hide */
 public class ParsedPermissionUtils {
@@ -103,17 +100,12 @@
                 if (resourceType.equals("array")) {
                     final String[] knownCerts = res.getStringArray(knownCertsResource);
                     if (knownCerts != null) {
-                        // Convert the provided digest to upper case for consistent Set membership
-                        // checks when verifying the signing certificate digests of requesting apps.
-                        permission.knownCerts = new ArraySet<>();
-                        for (String knownCert : knownCerts) {
-                            permission.knownCerts.add(knownCert.toUpperCase(Locale.US));
-                        }
+                        permission.setKnownCerts(knownCerts);
                     }
                 } else {
                     final String knownCert = res.getString(knownCertsResource);
                     if (knownCert != null) {
-                        permission.knownCerts = Set.of(knownCert.toUpperCase(Locale.US));
+                        permission.setKnownCert(knownCert);
                     }
                 }
                 if (permission.knownCerts == null) {
@@ -126,7 +118,7 @@
                 final String knownCert = sa.getString(
                         R.styleable.AndroidManifestPermission_knownCerts);
                 if (knownCert != null) {
-                    permission.knownCerts = Set.of(knownCert.toUpperCase(Locale.US));
+                    permission.setKnownCert(knownCert);
                 }
             }
 
diff --git a/core/java/android/content/pm/permission/OWNERS b/core/java/android/content/pm/permission/OWNERS
index cde7b2a..d302b0a 100644
--- a/core/java/android/content/pm/permission/OWNERS
+++ b/core/java/android/content/pm/permission/OWNERS
@@ -3,7 +3,6 @@
 toddke@android.com
 toddke@google.com
 patb@google.com
-moltmann@google.com
 svetoslavganov@android.com
 svetoslavganov@google.com
 zhanghai@google.com
diff --git a/core/java/android/hardware/usb/OWNERS b/core/java/android/hardware/usb/OWNERS
index 8f2b39d..8f5c2a0 100644
--- a/core/java/android/hardware/usb/OWNERS
+++ b/core/java/android/hardware/usb/OWNERS
@@ -1,4 +1,3 @@
 # Bug component: 175220
 
-moltmann@google.com
 badhri@google.com
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 6f89254..d631f68 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -5294,6 +5294,12 @@
                         pw.print(" flash=");
                         printmAh(pw, bs.flashlightPowerMah);
                     }
+                    if (bs.customMeasuredPowerMah != null) {
+                        for (int idx = 0; idx < bs.customMeasuredPowerMah.length; idx++) {
+                            pw.print(" custom[" + idx + "]=");
+                            printmAh(pw, bs.customMeasuredPowerMah[idx]);
+                        }
+                    }
                     pw.print(" )");
                 }
 
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 6a76da2..eac03dc 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1902,7 +1902,8 @@
      * Retrieves the PSS memory used by the process as given by the smaps. Optionally supply a long
      * array of up to 3 entries to also receive (up to 3 values in order): the Uss and SwapPss and
      * Rss (only filled in as of {@link android.os.Build.VERSION_CODES#P}) of the process, and
-     * another array to also retrieve the separate memtrack size.
+     * another array to also retrieve the separate memtrack sizes (up to 4 values in order): the
+     * total memtrack reported size, memtrack graphics, memtrack gl and memtrack other.
      *
      * @return The PSS memory usage, or 0 if failed to retrieve (i.e., given pid has gone).
      * @hide
@@ -2589,6 +2590,13 @@
     public static native long getIonPoolsSizeKb();
 
     /**
+     * Return GPU DMA buffer usage in kB or -1 on error.
+     *
+     * @hide
+     */
+    public static native long getGpuDmaBufUsageKb();
+
+    /**
      * Return DMA-BUF memory mapped by processes in kB.
      * Notes:
      *  * Warning: Might impact performance as it reads /proc/<pid>/maps files for each process.
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 0ff68fc..0589994 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -241,6 +241,13 @@
     }
 
     /**
+     * Checks if device supports V2 calls (e.g. PerUid).
+     */
+    public static boolean isV2Available() {
+        return nativeIsV2Available();
+    }
+
+    /**
      * Checks if Incremental installations are allowed.
      * A developer can disable Incremental installations by setting the property.
      */
@@ -439,6 +446,7 @@
 
     /* Native methods */
     private static native boolean nativeIsEnabled();
+    private static native boolean nativeIsV2Available();
     private static native boolean nativeIsIncrementalPath(@NonNull String path);
     private static native byte[] nativeUnsafeGetFileSignature(@NonNull String path);
 }
diff --git a/core/java/android/permission/OWNERS b/core/java/android/permission/OWNERS
index d09f351..b323468 100644
--- a/core/java/android/permission/OWNERS
+++ b/core/java/android/permission/OWNERS
@@ -1,6 +1,5 @@
 # Bug component: 137825
 
-moltmann@google.com
 evanseverson@google.com
 ntmyren@google.com
 zhanghai@google.com
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 85e9fdb..0e35ef9 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -693,13 +693,14 @@
         for (int usageNum = 0; usageNum < rawUsages.size(); usageNum++) {
             OpUsage usage = rawUsages.get(usageNum);
 
+            // If this attribution is a proxy, remove it
+            if (toRemoveProxies.contains(usage.toPackageAttr())) {
+                continue;
+            }
+
             // If this attribution has a special attribution, do not remove it
             if (specialAttributions.contains(usage.toPackageAttr())) {
                 deDuped.add(usage);
-            }
-
-            // If this attribution is a proxy, remove it
-            if (toRemoveProxies.contains(usage.toPackageAttr())) {
                 continue;
             }
 
diff --git a/core/java/android/permissionpresenterservice/OWNERS b/core/java/android/permissionpresenterservice/OWNERS
index d09f351..b323468 100644
--- a/core/java/android/permissionpresenterservice/OWNERS
+++ b/core/java/android/permissionpresenterservice/OWNERS
@@ -1,6 +1,5 @@
 # Bug component: 137825
 
-moltmann@google.com
 evanseverson@google.com
 ntmyren@google.com
 zhanghai@google.com
diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS
index 72f0983..28a24203 100644
--- a/core/java/android/print/OWNERS
+++ b/core/java/android/print/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 47273
 
-moltmann@google.com
 svetoslavganov@android.com
 svetoslavganov@google.com
diff --git a/core/java/android/print/pdf/OWNERS b/core/java/android/print/pdf/OWNERS
index 72f0983..28a24203 100644
--- a/core/java/android/print/pdf/OWNERS
+++ b/core/java/android/print/pdf/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 47273
 
-moltmann@google.com
 svetoslavganov@android.com
 svetoslavganov@google.com
diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS
index 72f0983..28a24203 100644
--- a/core/java/android/printservice/OWNERS
+++ b/core/java/android/printservice/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 47273
 
-moltmann@google.com
 svetoslavganov@android.com
 svetoslavganov@google.com
diff --git a/core/java/android/printservice/recommendation/OWNERS b/core/java/android/printservice/recommendation/OWNERS
index 72f0983..28a24203 100644
--- a/core/java/android/printservice/recommendation/OWNERS
+++ b/core/java/android/printservice/recommendation/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 47273
 
-moltmann@google.com
 svetoslavganov@android.com
 svetoslavganov@google.com
diff --git a/core/java/android/service/notification/NotificationListenerFilter.java b/core/java/android/service/notification/NotificationListenerFilter.java
index 6fdfaab..9de75ca 100644
--- a/core/java/android/service/notification/NotificationListenerFilter.java
+++ b/core/java/android/service/notification/NotificationListenerFilter.java
@@ -20,6 +20,7 @@
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
 
+import android.content.pm.VersionedPackage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
@@ -31,7 +32,8 @@
  */
 public class NotificationListenerFilter implements Parcelable {
     private int mAllowedNotificationTypes;
-    private ArraySet<String> mDisallowedPackages;
+    // VersionedPackage is holding the pkg name and pkg uid
+    private ArraySet<VersionedPackage> mDisallowedPackages;
 
     public NotificationListenerFilter() {
         mAllowedNotificationTypes = FLAG_FILTER_TYPE_CONVERSATIONS
@@ -41,7 +43,7 @@
         mDisallowedPackages = new ArraySet<>();
     }
 
-    public NotificationListenerFilter(int types, ArraySet<String> pkgs) {
+    public NotificationListenerFilter(int types, ArraySet<VersionedPackage> pkgs) {
         mAllowedNotificationTypes = types;
         mDisallowedPackages = pkgs;
     }
@@ -51,7 +53,8 @@
      */
     protected NotificationListenerFilter(Parcel in) {
         mAllowedNotificationTypes = in.readInt();
-        mDisallowedPackages = (ArraySet<String>) in.readArraySet(String.class.getClassLoader());
+        mDisallowedPackages = (ArraySet<VersionedPackage>) in.readArraySet(
+                VersionedPackage.class.getClassLoader());
     }
 
     @Override
@@ -77,7 +80,7 @@
         return (mAllowedNotificationTypes & type) != 0;
     }
 
-    public boolean isPackageAllowed(String pkg) {
+    public boolean isPackageAllowed(VersionedPackage pkg) {
         return !mDisallowedPackages.contains(pkg);
     }
 
@@ -85,7 +88,7 @@
         return mAllowedNotificationTypes;
     }
 
-    public ArraySet<String> getDisallowedPackages() {
+    public ArraySet<VersionedPackage> getDisallowedPackages() {
         return mDisallowedPackages;
     }
 
@@ -93,10 +96,18 @@
         mAllowedNotificationTypes = types;
     }
 
-    public void setDisallowedPackages(ArraySet<String> pkgs) {
+    public void setDisallowedPackages(ArraySet<VersionedPackage> pkgs) {
         mDisallowedPackages = pkgs;
     }
 
+    public void removePackage(VersionedPackage pkg) {
+        mDisallowedPackages.remove(pkg);
+    }
+
+    public void addPackage(VersionedPackage pkg) {
+        mDisallowedPackages.add(pkg);
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 64cddc3..f66f85b 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -80,6 +80,10 @@
  *     &lt;intent-filter>
  *         &lt;action android:name="android.service.notification.NotificationListenerService" />
  *     &lt;/intent-filter>
+ *     &lt;meta-data
+ *               android:name="android.service.notification.default_filter_types"
+ *               android:value="1,2">
+ *           &lt;/meta-data>
  * &lt;/service></pre>
  *
  * <p>The service should wait for the {@link #onListenerConnected()} event
@@ -103,6 +107,21 @@
     private final String TAG = getClass().getSimpleName();
 
     /**
+     * The name of the {@code meta-data} tag containing a comma separated list of default
+     * integer notification types that should be provided to this listener. See
+     * {@link #FLAG_FILTER_TYPE_ONGOING},
+     * {@link #FLAG_FILTER_TYPE_CONVERSATIONS}, {@link #FLAG_FILTER_TYPE_ALERTING),
+     * and {@link #FLAG_FILTER_TYPE_SILENT}.
+     * <p>This value will only be read if the app has not previously specified a default type list,
+     * and if the user has not overridden the allowed types.</p>
+     * <p>An absent value means 'allow all types'.
+     * A present but empty value means 'allow no types'.</p>
+     *
+     */
+    public static final String META_DATA_DEFAULT_FILTER_TYPES
+            = "android.service.notification.default_filter_types";
+
+    /**
      * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
      *     Normal interruption filter.
      */
@@ -254,23 +273,19 @@
     /**
      * A flag value indicating that this notification listener can see conversation type
      * notifications.
-     * @hide
      */
     public static final int FLAG_FILTER_TYPE_CONVERSATIONS = 1;
     /**
      * A flag value indicating that this notification listener can see altering type notifications.
-     * @hide
      */
     public static final int FLAG_FILTER_TYPE_ALERTING = 2;
     /**
      * A flag value indicating that this notification listener can see silent type notifications.
-     * @hide
      */
     public static final int FLAG_FILTER_TYPE_SILENT = 4;
     /**
      * A flag value indicating that this notification listener can see important
      * ( > {@link NotificationManager#IMPORTANCE_MIN}) ongoing type notifications.
-     * @hide
      */
     public static final int FLAG_FILTER_TYPE_ONGOING = 8;
 
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 6e9bc84..cba6af9 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -499,7 +499,7 @@
         }
 
         public String getPerfettoTrigger() {
-            return String.format("interaction-jank-monitor-%d", mCujType);
+            return String.format("com.android.telemetry.interaction-jank-monitor-%d", mCujType);
         }
 
         public String getName() {
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index af61f91..9b88516 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -133,6 +133,7 @@
     public double wakeLockPowerMah;
     public double wifiPowerMah;
     public double systemServiceCpuPowerMah;
+    public double[] customMeasuredPowerMah;
 
     // Do not include this sipper in results because it is included
     // in an aggregate sipper.
@@ -251,6 +252,17 @@
         proportionalSmearMah += other.proportionalSmearMah;
         totalSmearedPowerMah += other.totalSmearedPowerMah;
         systemServiceCpuPowerMah += other.systemServiceCpuPowerMah;
+        if (other.customMeasuredPowerMah != null) {
+            if (customMeasuredPowerMah == null) {
+                customMeasuredPowerMah = new double[other.customMeasuredPowerMah.length];
+            }
+            if (customMeasuredPowerMah.length == other.customMeasuredPowerMah.length) {
+                // This should always be true.
+                for (int idx = 0; idx < other.customMeasuredPowerMah.length; idx++) {
+                    customMeasuredPowerMah[idx] += other.customMeasuredPowerMah[idx];
+                }
+            }
+        }
     }
 
     /**
@@ -264,6 +276,11 @@
                 sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +
                 flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah
                 + systemServiceCpuPowerMah;
+        if (customMeasuredPowerMah != null) {
+            for (int idx = 0; idx < customMeasuredPowerMah.length; idx++) {
+                totalPowerMah += customMeasuredPowerMah[idx];
+            }
+        }
         totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah;
 
         return totalPowerMah;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index aa5015a..b20f50d 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -347,6 +347,7 @@
             mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
             mPowerCalculators.add(new SystemServicePowerCalculator(mPowerProfile));
             mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
+            mPowerCalculators.add(new CustomMeasuredPowerCalculator(mPowerProfile));
 
             mPowerCalculators.add(new UserPowerCalculator());
         }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 96a71b9..a39d19e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -13002,16 +13002,17 @@
         mWakeLockAllocationsUs = null;
         final long startTimeMs = mClocks.uptimeMillis();
         final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+        final List<Integer> uidsToRemove = new ArrayList<>();
         mCpuUidFreqTimeReader.readDelta((uid, cpuFreqTimeMs) -> {
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
-                mCpuUidFreqTimeReader.removeUid(uid);
+                uidsToRemove.add(uid);
                 if (DEBUG) Slog.d(TAG, "Got freq readings for an isolated uid: " + uid);
                 return;
             }
             if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
                 if (DEBUG) Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
-                mCpuUidFreqTimeReader.removeUid(uid);
+                uidsToRemove.add(uid);
                 return;
             }
             final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
@@ -13070,6 +13071,9 @@
                 }
             }
         });
+        for (int uid : uidsToRemove) {
+            mCpuUidFreqTimeReader.removeUid(uid);
+        }
 
         final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
         if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
@@ -13116,21 +13120,25 @@
     public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
         final long startTimeMs = mClocks.uptimeMillis();
         final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+        final List<Integer> uidsToRemove = new ArrayList<>();
         mCpuUidActiveTimeReader.readDelta((uid, cpuActiveTimesMs) -> {
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
-                mCpuUidActiveTimeReader.removeUid(uid);
+                uidsToRemove.add(uid);
                 if (DEBUG) Slog.w(TAG, "Got active times for an isolated uid: " + uid);
                 return;
             }
             if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
                 if (DEBUG) Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
-                mCpuUidActiveTimeReader.removeUid(uid);
+                uidsToRemove.add(uid);
                 return;
             }
             final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
             u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesMs, onBattery);
         });
+        for (int uid : uidsToRemove) {
+            mCpuUidActiveTimeReader.removeUid(uid);
+        }
 
         final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
         if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
@@ -13146,21 +13154,25 @@
     public void readKernelUidCpuClusterTimesLocked(boolean onBattery) {
         final long startTimeMs = mClocks.uptimeMillis();
         final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+        final List<Integer> uidsToRemove = new ArrayList<>();
         mCpuUidClusterTimeReader.readDelta((uid, cpuClusterTimesMs) -> {
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
-                mCpuUidClusterTimeReader.removeUid(uid);
+                uidsToRemove.add(uid);
                 if (DEBUG) Slog.w(TAG, "Got cluster times for an isolated uid: " + uid);
                 return;
             }
             if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
                 if (DEBUG) Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
-                mCpuUidClusterTimeReader.removeUid(uid);
+                uidsToRemove.add(uid);
                 return;
             }
             final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
             u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesMs, onBattery);
         });
+        for (int uid : uidsToRemove) {
+            mCpuUidClusterTimeReader.removeUid(uid);
+        }
 
         final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
         if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 094724c..2798b72 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -72,6 +72,7 @@
                 mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
                 mPowerCalculators.add(new SystemServicePowerCalculator(mPowerProfile));
                 mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
+                mPowerCalculators.add(new CustomMeasuredPowerCalculator(mPowerProfile));
 
                 mPowerCalculators.add(new UserPowerCalculator());
             }
diff --git a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
new file mode 100644
index 0000000..4babe8d
--- /dev/null
+++ b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.os.BatteryStats;
+
+/**
+ * Calculates the amount of power consumed by custom energy consumers (i.e. consumers of type
+ * {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
+ */
+public class CustomMeasuredPowerCalculator extends PowerCalculator {
+    public CustomMeasuredPowerCalculator(PowerProfile powerProfile) {
+    }
+
+    @Override
+    protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+            long rawUptimeUs, int statsType) {
+        updateCustomMeasuredPowerMah(app, u.getCustomMeasuredEnergiesMicroJoules());
+    }
+
+    private void updateCustomMeasuredPowerMah(BatterySipper sipper, long[] measuredEnergiesUJ) {
+        sipper.customMeasuredPowerMah = calculateMeasuredEnergiesMah(measuredEnergiesUJ);
+    }
+
+    private double[] calculateMeasuredEnergiesMah(long[] measuredEnergiesUJ) {
+        if (measuredEnergiesUJ == null) {
+            return null;
+        }
+        final double[] measuredEnergiesMah = new double[measuredEnergiesUJ.length];
+        for (int i = 0; i < measuredEnergiesUJ.length; i++) {
+            measuredEnergiesMah[i] = uJtoMah(measuredEnergiesUJ[i]);
+        }
+        return measuredEnergiesMah;
+    }
+}
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 488b18d..c6a040c 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -47,6 +47,13 @@
     private CollectionUtils() { /* cannot be instantiated */ }
 
     /**
+     * @see Collection#contains(Object)
+     */
+    public static <T> boolean contains(@Nullable Collection<T> collection, T element) {
+        return collection != null && collection.contains(element);
+    }
+
+    /**
      * Returns a list of items from the provided list that match the given condition.
      *
      * This is similar to {@link Stream#filter} but without the overhead of creating an intermediate
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index f42f468..dc6880e 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -219,7 +219,7 @@
     }
 
     private static String getTraceTriggerNameForAction(@Action int action) {
-        return "latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
+        return "com.android.telemetry.latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
     }
 
     public static boolean isEnabled(Context ctx) {
diff --git a/core/java/com/android/internal/util/PerfettoTrigger.java b/core/java/com/android/internal/util/PerfettoTrigger.java
index 9c87c69..c758504 100644
--- a/core/java/com/android/internal/util/PerfettoTrigger.java
+++ b/core/java/com/android/internal/util/PerfettoTrigger.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.util;
 
+import android.os.SystemClock;
 import android.util.Log;
 
 import java.io.IOException;
@@ -27,16 +28,28 @@
 public class PerfettoTrigger {
     private static final String TAG = "PerfettoTrigger";
     private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
+    private static final long THROTTLE_MILLIS = 60000;
+    private static volatile long sLastTriggerTime = -THROTTLE_MILLIS;
 
     /**
      * @param triggerName The name of the trigger. Must match the value defined in the AOT
      *                    Perfetto config.
      */
     public static void trigger(String triggerName) {
+        // Trace triggering has a non-negligible cost (fork+exec).
+        // To mitigate potential excessive triggering by the API client we ignore calls that happen
+        // too quickl after the most recent trigger.
+        long sinceLastTrigger = SystemClock.elapsedRealtime() - sLastTriggerTime;
+        if (sinceLastTrigger < THROTTLE_MILLIS) {
+            Log.v(TAG, "Not triggering " + triggerName + " - not enough time since last trigger");
+            return;
+        }
+
         try {
             ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
             Log.v(TAG, "Triggering " + String.join(" ", pb.command()));
-            Process process = pb.start();
+            pb.start();
+            sLastTriggerTime = SystemClock.elapsedRealtime();
         } catch (IOException e) {
             Log.w(TAG, "Failed to trigger " + triggerName, e);
         }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index cb586d6..8982519 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -1236,6 +1236,8 @@
 
         if (IncrementalManager.isFeatureEnabled()) {
             addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY, 0);
+            addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY_VERSION,
+                    IncrementalManager.isV2Available() ? 2 : 1);
         }
 
         if (PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 8edc8a1..9510f27 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -207,6 +207,7 @@
             ],
 
             shared_libs: [
+                "android.hardware.memtrack-unstable-ndk_platform",
                 "audioclient-types-aidl-cpp",
                 "audioflinger-aidl-cpp",
                 "av-types-aidl-cpp",
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index c64174b..a7950f2 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -33,6 +33,7 @@
 #include <string>
 #include <vector>
 
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
 #include <android-base/logging.h>
 #include <bionic/malloc.h>
 #include <debuggerd/client.h>
@@ -45,6 +46,7 @@
 #include "jni.h"
 #include <dmabufinfo/dmabuf_sysfs_stats.h>
 #include <dmabufinfo/dmabufinfo.h>
+#include <dmabufinfo/dmabuf_sysfs_stats.h>
 #include <meminfo/procmeminfo.h>
 #include <meminfo/sysmeminfo.h>
 #include <memtrack/memtrack.h>
@@ -520,14 +522,15 @@
     }
 
     if (outUssSwapPssRss != NULL) {
-        if (env->GetArrayLength(outUssSwapPssRss) >= 1) {
+        int outLen = env->GetArrayLength(outUssSwapPssRss);
+        if (outLen >= 1) {
             jlong* outUssSwapPssRssArray = env->GetLongArrayElements(outUssSwapPssRss, 0);
             if (outUssSwapPssRssArray != NULL) {
                 outUssSwapPssRssArray[0] = uss;
-                if (env->GetArrayLength(outUssSwapPssRss) >= 2) {
+                if (outLen >= 2) {
                     outUssSwapPssRssArray[1] = swapPss;
                 }
-                if (env->GetArrayLength(outUssSwapPssRss) >= 3) {
+                if (outLen >= 3) {
                     outUssSwapPssRssArray[2] = rss;
                 }
             }
@@ -536,10 +539,20 @@
     }
 
     if (outMemtrack != NULL) {
-        if (env->GetArrayLength(outMemtrack) >= 1) {
+        int outLen = env->GetArrayLength(outMemtrack);
+        if (outLen >= 1) {
             jlong* outMemtrackArray = env->GetLongArrayElements(outMemtrack, 0);
             if (outMemtrackArray != NULL) {
                 outMemtrackArray[0] = memtrack;
+                if (outLen >= 2) {
+                    outMemtrackArray[1] = graphics_mem.graphics;
+                }
+                if (outLen >= 3) {
+                    outMemtrackArray[2] = graphics_mem.gl;
+                }
+                if (outLen >= 4) {
+                    outMemtrackArray[3] = graphics_mem.other;
+                }
             }
             env->ReleaseLongArrayElements(outMemtrack, outMemtrackArray, 0);
         }
@@ -838,6 +851,31 @@
     return poolsSizeKb;
 }
 
+static jlong android_os_Debug_getGpuDmaBufUsageKb(JNIEnv* env, jobject clazz) {
+    std::vector<aidl::android::hardware::memtrack::DeviceInfo> gpu_device_info;
+    if (!memtrack_gpu_device_info(&gpu_device_info)) {
+        return -1;
+    }
+
+    dmabufinfo::DmabufSysfsStats stats;
+    if (!GetDmabufSysfsStats(&stats)) {
+        return -1;
+    }
+
+    jlong sizeKb = 0;
+    const auto& importer_stats = stats.importer_info();
+    for (const auto& dev_info : gpu_device_info) {
+        const auto& importer_info = importer_stats.find(dev_info.name);
+        if (importer_info == importer_stats.end()) {
+            continue;
+        }
+
+        sizeKb += importer_info->second.size;
+    }
+
+    return sizeKb;
+}
+
 static jlong android_os_Debug_getDmabufMappedSizeKb(JNIEnv* env, jobject clazz) {
     jlong dmabufPss = 0;
     std::vector<dmabufinfo::DmaBuffer> dmabufs;
@@ -946,6 +984,8 @@
             (void*)android_os_Debug_getIonHeapsSizeKb },
     { "getDmabufTotalExportedKb", "()J",
             (void*)android_os_Debug_getDmabufTotalExportedKb },
+    { "getGpuDmaBufUsageKb", "()J",
+            (void*)android_os_Debug_getGpuDmaBufUsageKb },
     { "getIonPoolsSizeKb", "()J",
             (void*)android_os_Debug_getIonPoolsSizeKb },
     { "getDmabufMappedSizeKb", "()J",
diff --git a/core/jni/android_os_incremental_IncrementalManager.cpp b/core/jni/android_os_incremental_IncrementalManager.cpp
index 44bff01..2384efa 100644
--- a/core/jni/android_os_incremental_IncrementalManager.cpp
+++ b/core/jni/android_os_incremental_IncrementalManager.cpp
@@ -30,6 +30,10 @@
     return IncFs_IsEnabled();
 }
 
+static jboolean nativeIsV2Available(JNIEnv* env, jobject clazz) {
+    return !!(IncFs_Features() & INCFS_FEATURE_V2);
+}
+
 static jboolean nativeIsIncrementalPath(JNIEnv* env,
                                     jobject clazz,
                                     jstring javaPath) {
@@ -53,12 +57,12 @@
     return result;
 }
 
-static const JNINativeMethod method_table[] = {{"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
-                                               {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z",
-                                                (void*)nativeIsIncrementalPath},
-                                               {"nativeUnsafeGetFileSignature",
-                                                "(Ljava/lang/String;)[B",
-                                                (void*)nativeUnsafeGetFileSignature}};
+static const JNINativeMethod method_table[] =
+        {{"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
+         {"nativeIsV2Available", "()Z", (void*)nativeIsV2Available},
+         {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z", (void*)nativeIsIncrementalPath},
+         {"nativeUnsafeGetFileSignature", "(Ljava/lang/String;)[B",
+          (void*)nativeUnsafeGetFileSignature}};
 
 int register_android_os_incremental_IncrementalManager(JNIEnv* env) {
     return jniRegisterNativeMethods(env, "android/os/incremental/IncrementalManager",
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 09ca12a..b7c755e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1590,8 +1590,8 @@
          take precedence over lower ones.
          See com.android.server.timedetector.TimeDetectorStrategy for available sources. -->
     <string-array name="config_autoTimeSourcesPriority">
-        <item>telephony</item>
         <item>network</item>
+        <item>telephony</item>
     </string-array>
 
     <!-- Enables the GnssTimeUpdate service. This is the global switch for enabling Gnss time based
diff --git a/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java b/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java
new file mode 100644
index 0000000..606e81d
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.util.ArraySet;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public final class PermissionInfoTest {
+    private static final String KNOWN_CERT_DIGEST_1 =
+            "6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599";
+    private static final String KNOWN_CERT_DIGEST_2 =
+            "9369370ffcfdc1e92dae777252c05c483b8cbb55fa9d5fd9f6317f623ae6d8c6";
+
+    @Test
+    public void createFromParcel_returnsKnownCerts() {
+        // The platform supports a knownSigner permission protection flag that allows one or more
+        // trusted signing certificates to be specified with the permission declaration; if a
+        // requesting app is signed by any of these trusted certificates the permission is granted.
+        // This test verifies the Set of knownCerts is properly parceled / unparceled.
+        PermissionInfo permissionInfo = new PermissionInfo();
+        permissionInfo.protectionLevel =
+                PermissionInfo.PROTECTION_SIGNATURE | PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER;
+        permissionInfo.knownCerts = new ArraySet<>(2);
+        permissionInfo.knownCerts.add(KNOWN_CERT_DIGEST_1);
+        permissionInfo.knownCerts.add(KNOWN_CERT_DIGEST_2);
+        Parcel parcel = Parcel.obtain();
+        permissionInfo.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        PermissionInfo unparceledPermissionInfo = PermissionInfo.CREATOR.createFromParcel(parcel);
+
+        assertNotNull(unparceledPermissionInfo.knownCerts);
+        assertEquals(2, unparceledPermissionInfo.knownCerts.size());
+        assertTrue(unparceledPermissionInfo.knownCerts.contains(KNOWN_CERT_DIGEST_1));
+        assertTrue(unparceledPermissionInfo.knownCerts.contains(KNOWN_CERT_DIGEST_2));
+    }
+}
diff --git a/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java b/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java
index a43b238..a121941 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java
@@ -16,14 +16,14 @@
 
 package android.service.notification;
 
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.app.NotificationChannel;
+import android.content.pm.VersionedPackage;
 import android.os.Parcel;
 import android.util.ArraySet;
 
@@ -45,16 +45,19 @@
         assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isTrue();
         assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS
                 | FLAG_FILTER_TYPE_ALERTING
-                | FLAG_FILTER_TYPE_SILENT);
+                | FLAG_FILTER_TYPE_SILENT
+                | FLAG_FILTER_TYPE_ONGOING);
 
         assertThat(nlf.getDisallowedPackages()).isEmpty();
-        assertThat(nlf.isPackageAllowed("pkg1")).isTrue();
+        assertThat(nlf.isPackageAllowed(new VersionedPackage("any", 0))).isTrue();
     }
 
 
     @Test
     public void testConstructor() {
-        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
+        VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+        VersionedPackage a2= new VersionedPackage("pkg2", 2142534);
+        ArraySet<VersionedPackage> pkgs = new ArraySet<>(new VersionedPackage[] {a1, a2});
         NotificationListenerFilter nlf =
                 new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
         assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isFalse();
@@ -62,20 +65,21 @@
         assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isFalse();
         assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);
 
-        assertThat(nlf.getDisallowedPackages()).contains("pkg1");
-        assertThat(nlf.getDisallowedPackages()).contains("pkg2");
-        assertThat(nlf.isPackageAllowed("pkg1")).isFalse();
-        assertThat(nlf.isPackageAllowed("pkg2")).isFalse();
+        assertThat(nlf.getDisallowedPackages()).contains(a1);
+        assertThat(nlf.getDisallowedPackages()).contains(a2);
+        assertThat(nlf.isPackageAllowed(a1)).isFalse();
+        assertThat(nlf.isPackageAllowed(a2)).isFalse();
     }
 
     @Test
     public void testSetDisallowedPackages() {
         NotificationListenerFilter nlf = new NotificationListenerFilter();
 
-        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1"});
+        ArraySet<VersionedPackage> pkgs = new ArraySet<>(
+                new VersionedPackage[] {new VersionedPackage("pkg1", 0)});
         nlf.setDisallowedPackages(pkgs);
 
-        assertThat(nlf.isPackageAllowed("pkg1")).isFalse();
+        assertThat(nlf.isPackageAllowed(new VersionedPackage("pkg1", 0))).isFalse();
     }
 
     @Test
@@ -94,7 +98,9 @@
     @Test
     public void testDescribeContents() {
         final int expected = 0;
-        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
+        VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+        VersionedPackage a2= new VersionedPackage("pkg2", 2142534);
+        ArraySet<VersionedPackage> pkgs = new ArraySet<>(new VersionedPackage[] {a1, a2});
         NotificationListenerFilter nlf =
                 new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
         assertThat(nlf.describeContents()).isEqualTo(expected);
@@ -102,7 +108,9 @@
 
     @Test
     public void testParceling() {
-        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
+        VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+        VersionedPackage a2= new VersionedPackage("pkg2", 2142534);
+        ArraySet<VersionedPackage> pkgs = new ArraySet<>(new VersionedPackage[] {a1, a2});
         NotificationListenerFilter nlf =
                 new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
 
@@ -116,9 +124,9 @@
         assertThat(nlf1.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isFalse();
         assertThat(nlf1.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);
 
-        assertThat(nlf1.getDisallowedPackages()).contains("pkg1");
-        assertThat(nlf1.getDisallowedPackages()).contains("pkg2");
-        assertThat(nlf1.isPackageAllowed("pkg1")).isFalse();
-        assertThat(nlf1.isPackageAllowed("pkg2")).isFalse();
+        assertThat(nlf1.getDisallowedPackages()).contains(a1);
+        assertThat(nlf1.getDisallowedPackages()).contains(a2);
+        assertThat(nlf1.isPackageAllowed(a1)).isFalse();
+        assertThat(nlf1.isPackageAllowed(a2)).isFalse();
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/widget/OWNERS b/core/tests/coretests/src/com/android/internal/widget/OWNERS
new file mode 100644
index 0000000..b40fe24
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/OWNERS
@@ -0,0 +1,3 @@
+# LockSettings related
+per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS
+per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 65d3a01..549e074 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -6,7 +6,6 @@
 jsharkey@android.com
 jsharkey@google.com
 lorenzo@google.com
-moltmann@google.com
 svetoslavganov@android.com
 svetoslavganov@google.com
 toddke@android.com
diff --git a/data/etc/car/com.android.car.provision.xml b/data/etc/car/com.android.car.provision.xml
index 4fd9cae..42cfd3c 100644
--- a/data/etc/car/com.android.car.provision.xml
+++ b/data/etc/car/com.android.car.provision.xml
@@ -17,6 +17,7 @@
 <permissions>
     <privapp-permissions package="com.android.car.provision">
         <permission name="android.car.permission.CAR_POWERTRAIN"/>
+        <permission name="android.permission.DISPATCH_PROVISIONING_MESSAGE"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
         <permission name="android.permission.MANAGE_USERS"/>
diff --git a/data/etc/com.android.emergency.xml b/data/etc/com.android.emergency.xml
index 734561c..fa92b6d 100644
--- a/data/etc/com.android.emergency.xml
+++ b/data/etc/com.android.emergency.xml
@@ -19,6 +19,7 @@
         <!-- Required to place emergency calls from emergency info screen. -->
         <permission name="android.permission.CALL_PRIVILEGED"/>
         <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
         <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/com.android.provision.xml b/data/etc/com.android.provision.xml
index d2ea0ec..68f8298 100644
--- a/data/etc/com.android.provision.xml
+++ b/data/etc/com.android.provision.xml
@@ -17,7 +17,7 @@
 <permissions>
     <privapp-permissions package="com.android.provision">
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
-        <permissionn ame="android.permission.DISPATCH_PROVISIONING_MESSAGE"/>
+        <permission name="android.permission.DISPATCH_PROVISIONING_MESSAGE"/>
         <permission name="android.permission.MASTER_CLEAR"/>
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index e473c55..3fdb0da 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -58,5 +58,6 @@
         <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
         <permission name="android.permission.READ_DREAM_STATE"/>
         <permission name="android.permission.READ_DREAM_SUPPRESSION"/>
+        <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
     </privapp-permissions>
 </permissions>
diff --git a/graphics/java/android/graphics/pdf/OWNERS b/graphics/java/android/graphics/pdf/OWNERS
index f04e200..057dc0d 100644
--- a/graphics/java/android/graphics/pdf/OWNERS
+++ b/graphics/java/android/graphics/pdf/OWNERS
@@ -5,4 +5,3 @@
 sumir@google.com
 svetoslavganov@android.com
 svetoslavganov@google.com
-moltmann@google.com
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 2391a08..047df5b 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
@@ -86,6 +86,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
@@ -586,11 +587,15 @@
             // There were no bubbles saved for this used.
             return;
         }
-        for (BubbleEntry e : mSysuiProxy.getShouldRestoredEntries(savedBubbleKeys)) {
-            if (canLaunchInActivityView(mContext, e)) {
-                updateBubble(e, true /* suppressFlyout */, false /* showInShade */);
-            }
-        }
+        mSysuiProxy.getShouldRestoredEntries(savedBubbleKeys, (entries) -> {
+            mMainExecutor.execute(() -> {
+                for (BubbleEntry e : entries) {
+                    if (canLaunchInActivityView(mContext, e)) {
+                        updateBubble(e, true /* suppressFlyout */, false /* showInShade */);
+                    }
+                }
+            });
+        });
         // Finally, remove the entries for this user now that bubbles are restored.
         mSavedBubbleKeysPerUser.remove(mCurrentUserId);
     }
@@ -856,21 +861,24 @@
         }
     }
 
-    private void onRankingUpdated(RankingMap rankingMap) {
+    private void onRankingUpdated(RankingMap rankingMap,
+            HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey) {
         if (mTmpRanking == null) {
             mTmpRanking = new NotificationListenerService.Ranking();
         }
         String[] orderedKeys = rankingMap.getOrderedKeys();
         for (int i = 0; i < orderedKeys.length; i++) {
             String key = orderedKeys[i];
-            BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(key);
+            Pair<BubbleEntry, Boolean> entryData = entryDataByKey.get(key);
+            BubbleEntry entry = entryData.first;
+            boolean shouldBubbleUp = entryData.second;
             rankingMap.getRanking(key, mTmpRanking);
             boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key);
             if (isActiveBubble && !mTmpRanking.canBubble()) {
                 // If this entry is no longer allowed to bubble, dismiss with the BLOCKED reason.
                 // This means that the app or channel's ability to bubble has been revoked.
                 mBubbleData.dismissBubbleWithKey(key, DISMISS_BLOCKED);
-            } else if (isActiveBubble && !mSysuiProxy.shouldBubbleUp(key)) {
+            } else if (isActiveBubble && !shouldBubbleUp) {
                 // If this entry is allowed to bubble, but cannot currently bubble up, dismiss it.
                 // This happens when DND is enabled and configured to hide bubbles. Dismissing with
                 // the reason DISMISS_NO_BUBBLE_UP will retain the underlying notification, so that
@@ -919,17 +927,20 @@
     private void setIsBubble(@NonNull final Bubble b, final boolean isBubble) {
         Objects.requireNonNull(b);
         b.setIsBubble(isBubble);
-        final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(b.getKey());
-        if (entry != null) {
-            // Updating the entry to be a bubble will trigger our normal update flow
-            setIsBubble(entry, isBubble, b.shouldAutoExpand());
-        } else if (isBubble) {
-            // If bubble doesn't exist, it's a persisted bubble so we need to add it to the
-            // stack ourselves
-            Bubble bubble = mBubbleData.getOrCreateBubble(null, b /* persistedBubble */);
-            inflateAndAdd(bubble, bubble.shouldAutoExpand() /* suppressFlyout */,
-                    !bubble.shouldAutoExpand() /* showInShade */);
-        }
+        mSysuiProxy.getPendingOrActiveEntry(b.getKey(), (entry) -> {
+            mMainExecutor.execute(() -> {
+                if (entry != null) {
+                    // Updating the entry to be a bubble will trigger our normal update flow
+                    setIsBubble(entry, isBubble, b.shouldAutoExpand());
+                } else if (isBubble) {
+                    // If bubble doesn't exist, it's a persisted bubble so we need to add it to the
+                    // stack ourselves
+                    Bubble bubble = mBubbleData.getOrCreateBubble(null, b /* persistedBubble */);
+                    inflateAndAdd(bubble, bubble.shouldAutoExpand() /* suppressFlyout */,
+                            !bubble.shouldAutoExpand() /* showInShade */);
+                }
+            });
+        });
     }
 
     @SuppressWarnings("FieldCanBeLocal")
@@ -992,14 +1003,17 @@
                     }
 
                 }
-                final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey());
-                if (entry != null) {
-                    final String groupKey = entry.getStatusBarNotification().getGroupKey();
-                    if (getBubblesInGroup(groupKey).isEmpty()) {
-                        // Time to potentially remove the summary
-                        mSysuiProxy.notifyMaybeCancelSummary(bubble.getKey());
-                    }
-                }
+                mSysuiProxy.getPendingOrActiveEntry(bubble.getKey(), (entry) -> {
+                    mMainExecutor.execute(() -> {
+                        if (entry != null) {
+                            final String groupKey = entry.getStatusBarNotification().getGroupKey();
+                            if (getBubblesInGroup(groupKey).isEmpty()) {
+                                // Time to potentially remove the summary
+                                mSysuiProxy.notifyMaybeCancelSummary(bubble.getKey());
+                            }
+                        }
+                    });
+                });
             }
             mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);
 
@@ -1121,23 +1135,6 @@
         mStackView.updateContentDescription();
     }
 
-    /**
-     * The task id of the expanded view, if the stack is expanded and not occluded by the
-     * status bar, otherwise returns {@link ActivityTaskManager#INVALID_TASK_ID}.
-     */
-    private int getExpandedTaskId() {
-        if (mStackView == null) {
-            return INVALID_TASK_ID;
-        }
-        final BubbleViewProvider expandedViewProvider = mStackView.getExpandedBubble();
-        if (expandedViewProvider != null && isStackExpanded()
-                && !mStackView.isExpansionAnimating()
-                && !mSysuiProxy.isNotificationShadeExpand()) {
-            return expandedViewProvider.getTaskId();
-        }
-        return INVALID_TASK_ID;
-    }
-
     @VisibleForTesting
     public BubbleStackView getStackView() {
         return mStackView;
@@ -1343,9 +1340,10 @@
         }
 
         @Override
-        public void onRankingUpdated(RankingMap rankingMap) {
+        public void onRankingUpdated(RankingMap rankingMap,
+                HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey) {
             mMainExecutor.execute(() -> {
-                BubbleController.this.onRankingUpdated(rankingMap);
+                BubbleController.this.onRankingUpdated(rankingMap, entryDataByKey);
             });
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 6a1026b..8e061e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -26,6 +26,7 @@
 import android.os.Looper;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.view.View;
 
 import androidx.annotation.IntDef;
@@ -37,6 +38,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
+import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.function.BiConsumer;
@@ -182,8 +184,11 @@
      * permissions on the notification channel or the global setting.
      *
      * @param rankingMap the updated ranking map from NotificationListenerService
+     * @param entryDataByKey a map of ranking key to bubble entry and whether the entry should
+     *                       bubble up
      */
-    void onRankingUpdated(RankingMap rankingMap);
+    void onRankingUpdated(RankingMap rankingMap,
+            HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey);
 
     /**
      * Called when the status bar has become visible or invisible (either permanently or
@@ -243,14 +248,10 @@
 
     /** Callback to tell SysUi components execute some methods. */
     interface SysuiProxy {
-        @Nullable
-        BubbleEntry getPendingOrActiveEntry(String key);
+        void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback);
 
-        List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys);
-
-        boolean isNotificationShadeExpand();
-
-        boolean shouldBubbleUp(String key);
+        void getShouldRestoredEntries(ArraySet<String> savedBubbleKeys,
+                Consumer<List<BubbleEntry>> callback);
 
         void setNotificationInterruption(String key);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
index 94c6f01..c8f8987 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
@@ -39,7 +39,6 @@
 import android.window.TaskOrganizer;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
-import android.window.WindowOrganizer;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.wm.shell.common.SyncTransactionQueue;
@@ -116,7 +115,7 @@
     void applyResizeSplits(int position, LegacySplitDisplayLayout splitLayout) {
         WindowContainerTransaction t = new WindowContainerTransaction();
         splitLayout.resizeSplits(position, t);
-        new WindowOrganizer().applyTransaction(t);
+        applySyncTransaction(t);
     }
 
     boolean getHomeAndRecentsTasks(List<ActivityManager.RunningTaskInfo> out,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index a6f44ef..b7fd3cb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -43,6 +43,7 @@
 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.getNavigationBarRect;
 
+import android.annotation.BinderThread;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.TaskDescription;
@@ -498,7 +499,7 @@
         }
     }
 
-    @ExternalThread
+    @BinderThread
     static class Window extends BaseIWindow {
         private TaskSnapshotWindow mOuter;
 
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 1dc5cd9..2e4d7f62 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -239,14 +239,12 @@
 
 static jlong RuntimeShader_createShaderBuilder(JNIEnv* env, jobject, jstring sksl) {
     ScopedUtfChars strSksl(env, sksl);
-    auto result = SkRuntimeEffect::Make(SkString(strSksl.c_str()));
-    sk_sp<SkRuntimeEffect> effect = std::get<0>(result);
-    if (effect.get() == nullptr) {
-        const auto& err = std::get<1>(result);
-        doThrowIAE(env, err.c_str());
+    auto result = SkRuntimeEffect::Make(SkString(strSksl.c_str()), SkRuntimeEffect::Options{});
+    if (result.effect.get() == nullptr) {
+        doThrowIAE(env, result.errorText.c_str());
         return 0;
     }
-    return reinterpret_cast<jlong>(new SkRuntimeShaderBuilder(std::move(effect)));
+    return reinterpret_cast<jlong>(new SkRuntimeShaderBuilder(std::move(result.effect)));
 }
 
 static void SkRuntimeShaderBuilder_delete(SkRuntimeShaderBuilder* builder) {
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index cf2f0f0..a7ed091 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -10,7 +10,7 @@
           "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
         },
         {
-          "include-filter": "com.google.android.media.gts.WidevineYouTubePerformanceTests"
+          "include-filter": "com.google.android.media.gts.WidevineH264PlaybackTests"
         }
       ]
     }
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 314bf29..7a18bd5 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -225,6 +225,7 @@
     AStorageManager_unmountObb;
     ASurfaceControl_create; # introduced=29
     ASurfaceControl_createFromWindow; # introduced=29
+    ASurfaceControl_acquire; # introduced=31
     ASurfaceControl_release; # introduced=29
     ASurfaceTexture_acquireANativeWindow; # introduced=28
     ASurfaceTexture_attachToGLContext; # introduced=28
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 189be80..c1b5f1d 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -185,10 +185,16 @@
     return reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
 }
 
-void ASurfaceControl_release(ASurfaceControl* aSurfaceControl) {
-    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+void ASurfaceControl_acquire(ASurfaceControl* aSurfaceControl) {
+    SurfaceControl* surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
 
-    SurfaceControl_release(surfaceControl.get());
+    SurfaceControl_acquire(surfaceControl);
+}
+
+void ASurfaceControl_release(ASurfaceControl* aSurfaceControl) {
+    SurfaceControl* surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+
+    SurfaceControl_release(surfaceControl);
 }
 
 ASurfaceTransaction* ASurfaceTransaction_create() {
diff --git a/packages/PackageInstaller/OWNERS b/packages/PackageInstaller/OWNERS
index 252670a..8e1774b 100644
--- a/packages/PackageInstaller/OWNERS
+++ b/packages/PackageInstaller/OWNERS
@@ -1,5 +1,4 @@
 svetoslavganov@google.com
-moltmann@google.com
 toddke@google.com
 suprabh@google.com
 
diff --git a/packages/SettingsLib/res/drawable/ic_mobile_call_strength_1.xml b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_1.xml
new file mode 100644
index 0000000..46e2d45
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_1.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,8h2v3h-2z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.5,5h2v6h-2z"
+        android:fillAlpha="0.3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,3h2v8h-2z"
+        android:fillAlpha="0.3"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_mobile_call_strength_2.xml b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_2.xml
new file mode 100644
index 0000000..d9cd590
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_2.xml
@@ -0,0 +1,34 @@
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,8h2v3h-2z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.5,5h2v6h-2z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,3h2v8h-2z"
+        android:fillAlpha="0.3"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_mobile_call_strength_3.xml b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_3.xml
new file mode 100644
index 0000000..e80fd08
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_3.xml
@@ -0,0 +1,33 @@
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,8h2v3h-2z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.5,5h2v6h-2z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,3h2v8h-2z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_wifi_call_strength_1.xml b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_1.xml
new file mode 100644
index 0000000..493912b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_1.xml
@@ -0,0 +1,35 @@
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.05,9.59C17.69,9.22 17.19,9 16.64,9c-0.55,0 -1.05,0.22 -1.41,0.59L16.64,11L18.05,9.59z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.64,7.5c0.96,0 1.84,0.39 2.47,1.03l1.42,-1.42c-1,-1 -2.37,-1.61 -3.89,-1.61c-1.52,0 -2.89,0.62 -3.89,1.61l1.42,1.42C14.8,7.89 15.67,7.5 16.64,7.5z"
+        android:fillAlpha="0.3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.64,4c1.93,0 3.68,0.79 4.95,2.05L23,4.64C21.37,3.01 19.12,2 16.64,2c-2.49,0 -4.74,1.01 -6.36,2.64l1.42,1.42C12.96,4.79 14.71,4 16.64,4z"
+        android:fillAlpha="0.3"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_wifi_call_strength_2.xml b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_2.xml
new file mode 100644
index 0000000..af677fb
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_2.xml
@@ -0,0 +1,34 @@
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.05,9.59C17.69,9.22 17.19,9 16.64,9c-0.55,0 -1.05,0.22 -1.41,0.59L16.64,11L18.05,9.59z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.64,7.5c0.96,0 1.84,0.39 2.47,1.03l1.42,-1.42c-1,-1 -2.37,-1.61 -3.89,-1.61c-1.52,0 -2.89,0.62 -3.89,1.61l1.42,1.42C14.8,7.89 15.67,7.5 16.64,7.5z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.64,4c1.93,0 3.68,0.79 4.95,2.05L23,4.64C21.37,3.01 19.12,2 16.64,2c-2.49,0 -4.74,1.01 -6.36,2.64l1.42,1.42C12.96,4.79 14.71,4 16.64,4z"
+        android:fillAlpha="0.3"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_wifi_call_strength_3.xml b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_3.xml
new file mode 100644
index 0000000..68b39da
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_3.xml
@@ -0,0 +1,33 @@
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.05,9.59C17.69,9.22 17.19,9 16.64,9c-0.55,0 -1.05,0.22 -1.41,0.59L16.64,11L18.05,9.59z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.64,7.5c0.96,0 1.84,0.39 2.47,1.03l1.42,-1.42c-1,-1 -2.37,-1.61 -3.89,-1.61c-1.52,0 -2.89,0.62 -3.89,1.61l1.42,1.42C14.8,7.89 15.67,7.5 16.64,7.5z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.64,4c1.93,0 3.68,0.79 4.95,2.05L23,4.64C21.37,3.01 19.12,2 16.64,2c-2.49,0 -4.74,1.01 -6.36,2.64l1.42,1.42C12.96,4.79 14.71,4 16.64,4z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7556ace..3866151 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1380,7 +1380,7 @@
     <!-- Label for adding a new guest in the user switcher [CHAR LIMIT=35] -->
     <string name="guest_new_guest">Add guest</string>
     <!-- Label for exiting and removing the guest session in the user switcher [CHAR LIMIT=35] -->
-    <string name="guest_exit_guest">End guest session</string>
+    <string name="guest_exit_guest">Remove guest</string>
     <!-- Name for the guest user [CHAR LIMIT=35] -->
     <string name="guest_nickname">Guest</string>
 
@@ -1485,4 +1485,7 @@
     <string name="accessibility_ethernet_disconnected">Ethernet disconnected.</string>
     <!-- Content description of the Ethernet connection when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_ethernet_connected">Ethernet.</string>
+
+    <!-- Content description of the no calling for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_no_calling">No calling.</string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
index 45028ff..eff9e74 100644
--- a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
@@ -48,6 +48,8 @@
 
     public static final int WIFI_NO_CONNECTION = R.string.accessibility_no_wifi;
 
+    public static final int NO_CALLING = R.string.accessibility_no_calling;
+
     public static final int[] ETHERNET_CONNECTION_VALUES = {
         R.string.accessibility_ethernet_disconnected,
         R.string.accessibility_ethernet_connected,
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 4c7b898..0cd5e4d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -266,7 +266,7 @@
                                         serviceState.getDataRegState()) + ")")
                                         .append(',')
                 .append("signalStrength=").append(signalStrength == null ? ""
-                        : signalStrength.toString()).append(',')
+                        : signalStrength.getLevel()).append(',')
                 .append("telephonyDisplayInfo=").append(telephonyDisplayInfo == null ? ""
                         : telephonyDisplayInfo.toString()).append(']').toString();
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
index 0cb9906..e3413aa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
@@ -317,5 +317,21 @@
         ICON_NAME_TO_ICON.put("datadisable", DATA_DISABLED);
         ICON_NAME_TO_ICON.put("notdefaultdata", NOT_DEFAULT_DATA);
     }
+
+    public static final int[] WIFI_CALL_STRENGTH_ICONS = {
+        R.drawable.ic_wifi_call_strength_1,
+        R.drawable.ic_wifi_call_strength_1,
+        R.drawable.ic_wifi_call_strength_2,
+        R.drawable.ic_wifi_call_strength_3,
+        R.drawable.ic_wifi_call_strength_3
+    };
+
+    public static final int[] MOBILE_CALL_STRENGTH_ICONS = {
+        R.drawable.ic_mobile_call_strength_1,
+        R.drawable.ic_mobile_call_strength_1,
+        R.drawable.ic_mobile_call_strength_2,
+        R.drawable.ic_mobile_call_strength_3,
+        R.drawable.ic_mobile_call_strength_3
+    };
 }
 
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 6ba1fcb..34901f5 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -6,7 +6,6 @@
 svetoslavganov@google.com
 hackbod@google.com
 yamasani@google.com
-moltmann@google.com
 toddke@google.com
 cbrubaker@google.com
 omakoto@google.com
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 8166e35..a1191ae 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -139,6 +139,10 @@
     <!-- Size of shadows/elevations on keyguard -->
     <attr name="shadowRadius" format="float" />
 
+    <attr name="handleThickness" format="dimension" />
+    <attr name="handleColor" format="color" />
+    <attr name="scrimColor" format="color" />
+
     <!-- Used display CarrierText in Keyguard or QS Footer -->
     <declare-styleable name="CarrierText">
         <attr name="allCaps" format="boolean" />
@@ -173,15 +177,15 @@
     </declare-styleable>
 
     <declare-styleable name="CropView">
-        <attr name="handleThickness" format="dimension" />
-        <attr name="handleColor" format="color" />
-        <attr name="scrimColor" format="color" />
+        <attr name="handleThickness" />
+        <attr name="handleColor" />
+        <attr name="scrimColor" />
     </declare-styleable>
 
     <declare-styleable name="MagnifierView">
-        <attr name="handleThickness" format="dimension" />
-        <attr name="handleColor" format="color" />
-        <attr name="scrimColor" format="color" />
+        <attr name="handleThickness" />
+        <attr name="handleColor" />
+        <attr name="scrimColor" />
         <attr name="borderThickness" format="dimension" />
         <attr name="borderColor" format="color" />
     </declare-styleable>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index f83965e..6d731f8 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -18,7 +18,7 @@
 <resources>
     <bool name="are_flags_overrideable">false</bool>
 
-    <bool name="flag_notification_pipeline2">false</bool>
+    <bool name="flag_notification_pipeline2">true</bool>
     <bool name="flag_notification_pipeline2_rendering">false</bool>
     <bool name="flag_notif_updates">false</bool>
 
@@ -35,9 +35,6 @@
 
     <bool name="flag_brightness_slider">false</bool>
 
-    <!-- The new animations to/from lockscreen and AOD! -->
-    <bool name="flag_lockscreen_animations">false</bool>
-
     <!-- People Tile flag -->
     <bool name="flag_conversations">false</bool>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7c1c24b..d997ca2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1116,13 +1116,13 @@
     <string name="guest_exit_button">End guest session</string>
 
     <!-- Title of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
-    <string name="guest_exit_guest_dialog_title">End guest session?</string>
+    <string name="guest_exit_guest_dialog_title">Remove guest?</string>
 
     <!-- Message of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
     <string name="guest_exit_guest_dialog_message">All apps and data in this session will be deleted.</string>
 
     <!-- Label for button in confirmation dialog when exiting guest session [CHAR LIMIT=35] -->
-    <string name="guest_exit_guest_dialog_remove">End session</string>
+    <string name="guest_exit_guest_dialog_remove">Remove</string>
 
     <!-- Title of the notification when resuming an existing guest session [CHAR LIMIT=NONE] -->
     <string name="guest_wipe_session_title">Welcome back, guest!</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index eddcf8c..ae0b5d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -39,6 +39,7 @@
     private ImageView mMobileSignal;
     private ImageView mMobileRoaming;
     private CellSignalState mLastSignalState;
+    private boolean mProviderModel;
 
     public QSCarrier(Context context) {
         super(context);
@@ -59,15 +60,20 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mMobileGroup = findViewById(R.id.mobile_combo);
         if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
-            mMobileRoaming = findViewById(R.id.mobile_roaming_large);
+            mProviderModel = true;
         } else {
-            mMobileRoaming = findViewById(R.id.mobile_roaming);
+            mProviderModel = false;
         }
+        mMobileGroup = findViewById(R.id.mobile_combo);
+        mMobileRoaming = findViewById(R.id.mobile_roaming);
         mMobileSignal = findViewById(R.id.mobile_signal);
         mCarrierText = findViewById(R.id.qs_carrier_text);
-        mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
+        if (mProviderModel) {
+            mMobileSignal.setImageDrawable(mContext.getDrawable(R.drawable.ic_qs_no_calling_sms));
+        } else {
+            mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
+        }
     }
 
     /**
@@ -85,22 +91,27 @@
                     android.R.attr.textColorPrimary);
             mMobileRoaming.setImageTintList(colorStateList);
             mMobileSignal.setImageTintList(colorStateList);
-            mMobileSignal.setImageLevel(state.mobileSignalIconId);
 
-            StringBuilder contentDescription = new StringBuilder();
-            if (state.contentDescription != null) {
-                contentDescription.append(state.contentDescription).append(", ");
+            if (mProviderModel) {
+                mMobileSignal.setImageDrawable(mContext.getDrawable(state.mobileSignalIconId));
+                mMobileSignal.setContentDescription(state.contentDescription);
+            } else {
+                mMobileSignal.setImageLevel(state.mobileSignalIconId);
+                StringBuilder contentDescription = new StringBuilder();
+                if (state.contentDescription != null) {
+                    contentDescription.append(state.contentDescription).append(", ");
+                }
+                if (state.roaming) {
+                    contentDescription
+                            .append(mContext.getString(R.string.data_connection_roaming))
+                            .append(", ");
+                }
+                // TODO: show mobile data off/no internet text for 5 seconds before carrier text
+                if (hasValidTypeContentDescription(state.typeContentDescription)) {
+                    contentDescription.append(state.typeContentDescription);
+                }
+                mMobileSignal.setContentDescription(contentDescription);
             }
-            if (state.roaming) {
-                contentDescription
-                        .append(mContext.getString(R.string.data_connection_roaming))
-                        .append(", ");
-            }
-            // TODO: show mobile data off/no internet text for 5 seconds before carrier text
-            if (hasValidTypeContentDescription(state.typeContentDescription)) {
-                contentDescription.append(state.typeContentDescription);
-            }
-            mMobileSignal.setContentDescription(contentDescription);
         }
         return true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index 77200cc..a567f51 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -19,6 +19,7 @@
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
 
 import android.annotation.MainThread;
+import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Looper;
@@ -26,6 +27,7 @@
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.view.View;
 import android.widget.TextView;
@@ -33,6 +35,9 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.keyguard.CarrierTextController;
+import com.android.settingslib.AccessibilityContentDescriptions;
+import com.android.settingslib.mobile.TelephonyIcons;
+import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -62,6 +67,9 @@
             new CellSignalState[SIM_SLOTS];
     private View[] mCarrierDividers = new View[SIM_SLOTS - 1];
     private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
+    private int[] mLastSignalLevel = new int[SIM_SLOTS];
+    private String[] mLastSignalLevelDescription = new String[SIM_SLOTS];
+    private final boolean mProviderModel;
 
     private final NetworkController.SignalCallback mSignalCallback =
             new NetworkController.SignalCallback() {
@@ -72,6 +80,9 @@
                         CharSequence typeContentDescription,
                         CharSequence typeContentDescriptionHtml, CharSequence description,
                         boolean isWide, int subId, boolean roaming, boolean showTriangle) {
+                    if (mProviderModel) {
+                        return;
+                    }
                     int slotIndex = getSlotIndex(subId);
                     if (slotIndex >= SIM_SLOTS) {
                         Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
@@ -92,6 +103,46 @@
                 }
 
                 @Override
+                public void setCallIndicator(NetworkController.IconState statusIcon, int subId) {
+                    if (!mProviderModel) {
+                        return;
+                    }
+                    int slotIndex = getSlotIndex(subId);
+                    if (slotIndex >= SIM_SLOTS) {
+                        Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
+                        return;
+                    }
+                    if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                        Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
+                        return;
+                    }
+                    if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
+                        if (statusIcon.visible) {
+                            mInfos[slotIndex] = new CellSignalState(true,
+                                    statusIcon.icon, statusIcon.contentDescription, "", false);
+                        } else {
+                            // Whenever the no Calling & SMS state is cleared, switched to the last
+                            // known call strength icon.
+                            mInfos[slotIndex] = new CellSignalState(
+                                    true, mLastSignalLevel[slotIndex],
+                                    mLastSignalLevelDescription[slotIndex], "", false);
+                        }
+                        mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
+                    } else {
+                        mLastSignalLevel[slotIndex] = statusIcon.icon;
+                        mLastSignalLevelDescription[slotIndex] = statusIcon.contentDescription;
+                        // Only Shows the call strength icon when the no Calling & SMS icon is not
+                        // shown.
+                        if (mInfos[slotIndex].mobileSignalIconId
+                                != R.drawable.ic_qs_no_calling_sms) {
+                            mInfos[slotIndex] = new CellSignalState(true, statusIcon.icon,
+                                    statusIcon.contentDescription, "", false);
+                            mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
+                        }
+                    }
+                }
+
+                @Override
                 public void setNoSims(boolean hasNoSims, boolean simDetected) {
                     if (hasNoSims) {
                         for (int i = 0; i < SIM_SLOTS; i++) {
@@ -118,7 +169,12 @@
     private QSCarrierGroupController(QSCarrierGroup view, ActivityStarter activityStarter,
             @Background Handler bgHandler, @Main Looper mainLooper,
             NetworkController networkController,
-            CarrierTextController.Builder carrierTextControllerBuilder) {
+            CarrierTextController.Builder carrierTextControllerBuilder, Context context) {
+        if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+            mProviderModel = true;
+        } else {
+            mProviderModel = false;
+        }
         mActivityStarter = activityStarter;
         mBgHandler = bgHandler;
         mNetworkController = networkController;
@@ -149,7 +205,13 @@
         mCarrierDividers[1] = view.getCarrierDivider2();
 
         for (int i = 0; i < SIM_SLOTS; i++) {
-            mInfos[i] = new CellSignalState();
+            mInfos[i] = new CellSignalState(true, R.drawable.ic_qs_no_calling_sms,
+                    context.getText(AccessibilityContentDescriptions.NO_CALLING).toString(),
+                    "", false);
+            mLastSignalLevel[i] = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
+            mLastSignalLevelDescription[i] =
+                    context.getText(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0])
+                            .toString();
             mCarrierGroups[i].setOnClickListener(onClickListener);
         }
         view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
@@ -305,16 +367,18 @@
         private final Looper mLooper;
         private final NetworkController mNetworkController;
         private final CarrierTextController.Builder mCarrierTextControllerBuilder;
+        private final Context mContext;
 
         @Inject
         public Builder(ActivityStarter activityStarter, @Background Handler handler,
                 @Main Looper looper, NetworkController networkController,
-                CarrierTextController.Builder carrierTextControllerBuilder) {
+                CarrierTextController.Builder carrierTextControllerBuilder, Context context) {
             mActivityStarter = activityStarter;
             mHandler = handler;
             mLooper = looper;
             mNetworkController = networkController;
             mCarrierTextControllerBuilder = carrierTextControllerBuilder;
+            mContext = context;
         }
 
         public Builder setQSCarrierGroup(QSCarrierGroup view) {
@@ -324,7 +388,7 @@
 
         public QSCarrierGroupController build() {
             return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
-                    mNetworkController, mCarrierTextControllerBuilder);
+                    mNetworkController, mCarrierTextControllerBuilder, mContext);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index abf230e..d4bab21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -102,6 +102,12 @@
         public void onConfigChanged(Configuration newConfig) {
             mView.updateNavBackDrop(newConfig, mLightBarController);
             mView.updateResources();
+            if (mTileAdapter.updateNumColumns()) {
+                RecyclerView.LayoutManager lm = mView.getRecyclerView().getLayoutManager();
+                if (lm instanceof GridLayoutManager) {
+                    ((GridLayoutManager) lm).setSpanCount(mTileAdapter.getNumColumns());
+                }
+            }
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 21464fd..048fdc3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -98,7 +98,7 @@
     private final UiEventLogger mUiEventLogger;
     private final AccessibilityDelegateCompat mAccessibilityDelegate;
     private RecyclerView mRecyclerView;
-    private final int mNumColumns;
+    private int mNumColumns;
 
     @Inject
     public TileAdapter(Context context, QSTileHost qsHost, UiEventLogger uiEventLogger) {
@@ -123,6 +123,21 @@
         mRecyclerView = null;
     }
 
+    /**
+     * Update the number of columns to show, from resources.
+     *
+     * @return {@code true} if the number of columns changed, {@code false} otherwise
+     */
+    public boolean updateNumColumns() {
+        int numColumns = mContext.getResources().getInteger(R.integer.quick_settings_num_columns);
+        if (numColumns != mNumColumns) {
+            mNumColumns = numColumns;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     public int getNumColumns() {
         return mNumColumns;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
index f887151..f8f1d3a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
@@ -144,7 +144,9 @@
                 setAlpha(0f);
                 setTranslationX((getParentWidth() - getWidth()) / 2);
                 setVisibility(View.VISIBLE);
-                animate().alpha(1f).translationX(0).scaleX(1f).scaleY(1f).start();
+                boolean touchOnRight = event.getX() > getParentWidth() / 2;
+                float translateXTarget = touchOnRight ? 0 : getParentWidth() - getWidth();
+                animate().alpha(1f).translationX(translateXTarget).scaleX(1f).scaleY(1f).start();
                 break;
             case MotionEvent.ACTION_MOVE:
                 mLastCropPosition = cropPosition;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index d97f644..6d64049 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.screenshot;
 
+import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT;
+
 import android.annotation.IdRes;
 import android.annotation.UiThread;
 import android.content.ComponentName;
@@ -27,12 +29,23 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.ActionMode;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.SearchEvent;
 import android.view.View;
 import android.view.ViewTreeObserver.InternalInsetsInfo;
 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
 import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.ImageView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
@@ -123,8 +136,10 @@
         mCallback = callback;
 
         setContentView(R.layout.long_screenshot);
+        mWindow.setCallback(new WindowCallbacks(this::doFinish));
         mWindow.getDecorView().getViewTreeObserver()
                 .addOnComputeInternalInsetsListener(this);
+
         mPreview = findViewById(R.id.preview);
 
         mSave = findViewById(R.id.save);
@@ -334,4 +349,130 @@
             mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f);
         }
     }
+
+    private static class WindowCallbacks implements Window.Callback {
+
+        private final Runnable mFinish;
+
+        WindowCallbacks(Runnable finish) {
+            mFinish = finish;
+        }
+
+        @Override
+        public boolean dispatchKeyEvent(KeyEvent event) {
+            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+                if (DEBUG_INPUT) {
+                    Log.d(TAG, "onKeyEvent: KeyEvent.KEYCODE_BACK");
+                }
+                mFinish.run();
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean dispatchKeyShortcutEvent(KeyEvent event) {
+            return false;
+        }
+
+        @Override
+        public boolean dispatchTouchEvent(MotionEvent event) {
+            return false;
+        }
+
+        @Override
+        public boolean dispatchTrackballEvent(MotionEvent event) {
+            return false;
+        }
+
+        @Override
+        public boolean dispatchGenericMotionEvent(MotionEvent event) {
+            return false;
+        }
+
+        @Override
+        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+            return false;
+        }
+
+        @Nullable
+        @Override
+        public View onCreatePanelView(int featureId) {
+            return null;
+        }
+
+        @Override
+        public boolean onCreatePanelMenu(int featureId, @NonNull Menu menu) {
+            return false;
+        }
+
+        @Override
+        public boolean onPreparePanel(int featureId, @Nullable View view, @NonNull Menu menu) {
+            return false;
+        }
+
+        @Override
+        public boolean onMenuOpened(int featureId, @NonNull Menu menu) {
+            return false;
+        }
+
+        @Override
+        public boolean onMenuItemSelected(int featureId, @NonNull MenuItem item) {
+            return false;
+        }
+
+        @Override
+        public void onWindowAttributesChanged(WindowManager.LayoutParams attrs) {
+        }
+
+        @Override
+        public void onContentChanged() {
+        }
+
+        @Override
+        public void onWindowFocusChanged(boolean hasFocus) {
+        }
+
+        @Override
+        public void onAttachedToWindow() {
+        }
+
+        @Override
+        public void onDetachedFromWindow() {
+        }
+
+        @Override
+        public void onPanelClosed(int featureId, @NonNull Menu menu) {
+        }
+
+        @Override
+        public boolean onSearchRequested() {
+            return false;
+        }
+
+        @Override
+        public boolean onSearchRequested(SearchEvent searchEvent) {
+            return false;
+        }
+
+        @Nullable
+        @Override
+        public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
+            return null;
+        }
+
+        @Nullable
+        @Override
+        public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {
+            return null;
+        }
+
+        @Override
+        public void onActionModeStarted(ActionMode mode) {
+        }
+
+        @Override
+        public void onActionModeFinished(ActionMode mode) {
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 862c279..7aa41e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -67,10 +67,6 @@
         return mFlagReader.isEnabled(R.bool.flag_brightness_slider);
     }
 
-    public boolean useNewLockscreenAnimations() {
-        return mFlagReader.isEnabled(R.bool.flag_lockscreen_animations);
-    }
-
     public boolean isPeopleTileEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_conversations);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index c1feaca..2f0f90d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -11,10 +11,14 @@
 import android.graphics.PorterDuffXfermode
 import android.graphics.RadialGradient
 import android.graphics.Shader
+import android.os.SystemProperties
 import android.util.AttributeSet
 import android.view.View
 import com.android.systemui.Interpolators
 
+val enableLightReveal =
+        SystemProperties.getBoolean("persist.sysui.show_new_screen_on_transitions", false)
+
 /**
  * Provides methods to modify the various properties of a [LightRevealScrim] to reveal between 0% to
  * 100% of the view(s) underneath the scrim.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
index 0465ebf..edcf6d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
@@ -18,23 +18,19 @@
 
 import static android.service.notification.NotificationListenerService.Ranking;
 
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.ENABLE_NAS_FEEDBACK;
+
 import android.app.NotificationManager;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
 import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
 import android.util.Pair;
 
-import androidx.annotation.Nullable;
-
 import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.util.DeviceConfigProxy;
 
 import javax.inject.Inject;
 
@@ -45,10 +41,10 @@
  * should show an indicator.
  */
 @SysUISingleton
-public class AssistantFeedbackController extends ContentObserver {
-    private final Uri FEEDBACK_URI
-            = Settings.Global.getUriFor(Settings.Global.NOTIFICATION_FEEDBACK_ENABLED);
-    private ContentResolver mResolver;
+public class AssistantFeedbackController {
+    private final Context mContext;
+    private final Handler mHandler;
+    private final DeviceConfigProxy mDeviceConfigProxy;
 
     public static final int STATUS_UNCHANGED = 0;
     public static final int STATUS_ALERTED = 1;
@@ -56,34 +52,39 @@
     public static final int STATUS_PROMOTED = 3;
     public static final int STATUS_DEMOTED = 4;
 
-    private boolean mFeedbackEnabled;
+    private volatile boolean mFeedbackEnabled;
+
+    private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
+            new DeviceConfig.OnPropertiesChangedListener() {
+                @Override
+                public void onPropertiesChanged(DeviceConfig.Properties properties) {
+                    if (properties.getKeyset().contains(ENABLE_NAS_FEEDBACK)) {
+                        mFeedbackEnabled = properties.getBoolean(
+                                ENABLE_NAS_FEEDBACK, false);
+                    }
+                }
+            };
 
     /** Injected constructor */
     @Inject
-    public AssistantFeedbackController(Context context) {
-        super(new Handler(Looper.getMainLooper()));
-        mResolver = context.getContentResolver();
-        mResolver.registerContentObserver(FEEDBACK_URI, false, this, UserHandle.USER_ALL);
-        update(null);
+    public AssistantFeedbackController(@Main Handler handler,
+            Context context, DeviceConfigProxy proxy) {
+        mHandler = handler;
+        mContext = context;
+        mDeviceConfigProxy = proxy;
+        mFeedbackEnabled = mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+                ENABLE_NAS_FEEDBACK, false);
+        mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+                this::postToHandler, mPropertiesChangedListener);
     }
 
-    @Override
-    public void onChange(boolean selfChange, @Nullable Uri uri, int flags) {
-        update(uri);
-    }
-
-    @VisibleForTesting
-    public void update(@Nullable Uri uri) {
-        if (uri == null || FEEDBACK_URI.equals(uri)) {
-            mFeedbackEnabled = Settings.Global.getInt(mResolver,
-                    Settings.Global.NOTIFICATION_FEEDBACK_ENABLED, 0)
-                    != 0;
-        }
+    private void postToHandler(Runnable r) {
+        this.mHandler.post(r);
     }
 
     /**
-     * Determines whether to show any user controls related to the assistant. This is based on the
-     * settings flag {@link Settings.Global.NOTIFICATION_FEEDBACK_ENABLED}
+     * Determines whether to show any user controls related to the assistant based on the
+     * DeviceConfig flag value
      */
     public boolean isFeedbackEnabled() {
         return mFeedbackEnabled;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 85d8df8..8c2fa33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -29,7 +29,6 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.tuner.TunerService;
 
@@ -55,7 +54,6 @@
     private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
     private final Resources mResources;
     private final BatteryController mBatteryController;
-    private final FeatureFlags mFeatureFlags;
 
     private boolean mDozeAlwaysOn;
     private boolean mControlScreenOffAnimation;
@@ -67,8 +65,7 @@
             AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
             PowerManager powerManager,
             BatteryController batteryController,
-            TunerService tunerService,
-            FeatureFlags featureFlags) {
+            TunerService tunerService) {
         mResources = resources;
         mAmbientDisplayConfiguration = ambientDisplayConfiguration;
         mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -77,7 +74,6 @@
         mControlScreenOffAnimation = !getDisplayNeedsBlanking();
         mPowerManager = powerManager;
         mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
-        mFeatureFlags = featureFlags;
 
         tunerService.addTunable(
                 this,
@@ -204,7 +200,8 @@
      * then abruptly showing AOD.
      */
     public boolean shouldControlUnlockedScreenOff() {
-        return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations();
+        return getAlwaysOn() && SystemProperties.getBoolean(
+                "persist.sysui.show_new_screen_on_transitions", false);
     }
 
     private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 041a97e..e63902f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -35,6 +35,7 @@
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+import static com.android.systemui.statusbar.LightRevealScrimKt.getEnableLightReveal;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -179,7 +180,6 @@
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -440,7 +440,6 @@
     private final KeyguardViewMediator mKeyguardViewMediator;
     protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final BrightnessSlider.Factory mBrightnessSliderFactory;
-    private final FeatureFlags mFeatureFlags;
 
     private final List<ExpansionChangedListener> mExpansionChangedListeners;
 
@@ -763,8 +762,7 @@
             Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             NotificationIconAreaController notificationIconAreaController,
-            BrightnessSlider.Factory brightnessSliderFactory,
-            FeatureFlags featureFlags) {
+            BrightnessSlider.Factory brightnessSliderFactory) {
         super(context);
         mNotificationsController = notificationsController;
         mLightBarController = lightBarController;
@@ -842,7 +840,6 @@
         mDemoModeController = demoModeController;
         mNotificationIconAreaController = notificationIconAreaController;
         mBrightnessSliderFactory = brightnessSliderFactory;
-        mFeatureFlags = featureFlags;
 
         mExpansionChangedListeners = new ArrayList<>();
 
@@ -1184,11 +1181,9 @@
 
         mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
 
-        if (mFeatureFlags.useNewLockscreenAnimations() && mDozeParameters.getAlwaysOn()) {
+        if (getEnableLightReveal()) {
             mLightRevealScrim.setVisibility(View.VISIBLE);
             mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
-        } else {
-            mLightRevealScrim.setVisibility(View.GONE);
         }
 
         mNotificationPanelViewController.initDependencies(
@@ -3619,7 +3614,7 @@
 
     @Override
     public void onDozeAmountChanged(float linear, float eased) {
-        if (mFeatureFlags.useNewLockscreenAnimations()) {
+        if (getEnableLightReveal()) {
             mLightRevealScrim.setRevealAmount(1f - linear);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 00acd7b..8620376 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -42,8 +42,8 @@
 import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusBarWifiView;
 import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 
 import java.util.List;
@@ -66,7 +66,7 @@
     /**
      * Display the no calling & SMS icons.
      */
-    void setNoCallingIcons(String slot, List<NoCallingIconState> states);
+    void setCallIndicatorIcons(String slot, List<CallIndicatorIconState> states);
     public void setIconVisibility(String slot, boolean b);
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 5e8d590..f0c8527 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -34,8 +34,8 @@
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -206,6 +206,7 @@
         Collections.reverse(iconStates);
 
         for (MobileIconState state : iconStates) {
+
             StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId);
             if (holder == null) {
                 holder = StatusBarIconHolder.fromMobileIconState(state);
@@ -218,23 +219,25 @@
     }
 
     /**
-     * Accept a list of NoCallingIconStates, and show them in the same slot
+     * Accept a list of CallIndicatorIconStates, and show them in the same slot
      * @param slot StatusBar slot
      * @param states All of the no Calling & SMS icon states
      */
     @Override
-    public void setNoCallingIcons(String slot, List<NoCallingIconState> states) {
+    public void setCallIndicatorIcons(String slot, List<CallIndicatorIconState> states) {
         Slot noCallingSlot = getSlot(slot);
         int slotIndex = getSlotIndex(slot);
-
-        for (NoCallingIconState state : states) {
+        for (CallIndicatorIconState state : states) {
             StatusBarIconHolder holder = noCallingSlot.getHolderForTag(state.subId);
             if (holder == null) {
-                holder = StatusBarIconHolder.fromNoCallingState(mContext, state);
-                holder.setVisible(state.visible);
+                holder = StatusBarIconHolder.fromCallIndicatorState(mContext, state);
                 setIcon(slotIndex, holder);
             } else {
-                holder.setVisible(state.visible);
+                int resId = state.isNoCalling ? state.noCallingResId : state.callStrengthResId;
+                String contentDescription = state.isNoCalling
+                        ? state.noCallingDescription : state.callStrengthDescription;
+                holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
+                        Icon.createWithResource(mContext, resId), 0, 0, contentDescription));
                 setIcon(slotIndex, holder);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
index 36a0e63..a1a2d30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -22,8 +22,8 @@
 import android.os.UserHandle;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 
 /**
@@ -72,14 +72,18 @@
     }
 
     /**
-     * Creates a new StatusBarIconHolder from a NoCallingIconState.
+     * Creates a new StatusBarIconHolder from a CallIndicatorIconState.
      */
-    public static StatusBarIconHolder fromNoCallingState(
-            Context context, NoCallingIconState state) {
+    public static StatusBarIconHolder fromCallIndicatorState(
+            Context context, CallIndicatorIconState state) {
         StatusBarIconHolder holder = new StatusBarIconHolder();
+        int resId = state.isNoCalling ? state.noCallingResId : state.callStrengthResId;
+        String contentDescription = state.isNoCalling
+                ? state.noCallingDescription : state.callStrengthDescription;
         holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(),
-                Icon.createWithResource(context, state.resId), 0, 0, null);
+                Icon.createWithResource(context, resId), 0, 0, contentDescription);
         holder.mTag = state.subId;
+        holder.setVisible(true);
         return holder;
     }
 
@@ -92,6 +96,10 @@
         return mIcon;
     }
 
+    public void setIcon(StatusBarIcon icon) {
+        mIcon = icon;
+    }
+
     @Nullable
     public WifiIconState getWifiState() {
         return mWifiState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index f6165f6..7bc1bb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -22,6 +22,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.NetworkController;
@@ -39,7 +40,7 @@
 public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback,
         SecurityController.SecurityControllerCallback, Tunable {
     private static final String TAG = "StatusBarSignalPolicy";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.INFO);
 
     private final String mSlotAirplane;
     private final String mSlotMobile;
@@ -67,7 +68,8 @@
     private boolean mWifiVisible = false;
 
     private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>();
-    private ArrayList<NoCallingIconState> mNoCallingStates = new ArrayList<NoCallingIconState>();
+    private ArrayList<CallIndicatorIconState> mCallIndicatorStates =
+            new ArrayList<CallIndicatorIconState>();
     private WifiIconState mWifiIconState = new WifiIconState();
 
     public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) {
@@ -201,19 +203,25 @@
     }
 
     @Override
-    public void setNoCallingStatus(boolean noCalling, int subId) {
+    public void setCallIndicator(IconState statusIcon, int subId) {
         if (DEBUG) {
-            Log.d(TAG, "setNoCallingStatus: "
-                    + "noCalling = " + noCalling + ","
+            Log.d(TAG, "setCallIndicator: "
+                    + "statusIcon = " + statusIcon + ","
                     + "subId = " + subId);
         }
-        NoCallingIconState state = getNoCallingState(subId);
+        CallIndicatorIconState state = getNoCallingState(subId);
         if (state == null) {
             return;
         }
-        state.visible = noCalling;
-        mIconController.setNoCallingIcons(
-                mSlotNoCalling, NoCallingIconState.copyStates(mNoCallingStates));
+        if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
+            state.isNoCalling = statusIcon.visible;
+            state.noCallingDescription = statusIcon.contentDescription;
+        } else {
+            state.callStrengthResId = statusIcon.icon;
+            state.callStrengthDescription = statusIcon.contentDescription;
+        }
+        mIconController.setCallIndicatorIcons(
+                mSlotNoCalling, CallIndicatorIconState.copyStates(mCallIndicatorStates));
     }
 
     @Override
@@ -273,8 +281,8 @@
         }
     }
 
-    private NoCallingIconState getNoCallingState(int subId) {
-        for (NoCallingIconState state : mNoCallingStates) {
+    private CallIndicatorIconState getNoCallingState(int subId) {
+        for (CallIndicatorIconState state : mCallIndicatorStates) {
             if (state.subId == subId) {
                 return state;
             }
@@ -315,23 +323,25 @@
         }
 
         mIconController.removeAllIconsForSlot(mSlotMobile);
+        mIconController.removeAllIconsForSlot(mSlotNoCalling);
         mMobileStates.clear();
-        List<NoCallingIconState> noCallingStates = new ArrayList<NoCallingIconState>();
-        noCallingStates.addAll(mNoCallingStates);
-        mNoCallingStates.clear();
+        List<CallIndicatorIconState> noCallingStates = new ArrayList<CallIndicatorIconState>();
+        noCallingStates.addAll(mCallIndicatorStates);
+        mCallIndicatorStates.clear();
         final int n = subs.size();
         for (int i = 0; i < n; i++) {
             mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId()));
             boolean isNewSub = true;
-            for (NoCallingIconState state : noCallingStates) {
+            for (CallIndicatorIconState state : noCallingStates) {
                 if (state.subId == subs.get(i).getSubscriptionId()) {
-                    mNoCallingStates.add(state);
+                    mCallIndicatorStates.add(state);
                     isNewSub = false;
                     break;
                 }
             }
             if (isNewSub) {
-                mNoCallingStates.add(new NoCallingIconState(subs.get(i).getSubscriptionId()));
+                mCallIndicatorStates.add(
+                        new CallIndicatorIconState(subs.get(i).getSubscriptionId()));
             }
         }
     }
@@ -425,14 +435,18 @@
     /**
      * Stores the StatusBar state for no Calling & SMS.
      */
-    public static class NoCallingIconState {
-        public boolean visible;
-        public int resId;
+    public static class CallIndicatorIconState {
+        public boolean isNoCalling;
+        public int noCallingResId;
+        public int callStrengthResId;
         public int subId;
+        public String noCallingDescription;
+        public String callStrengthDescription;
 
-        private NoCallingIconState(int subId) {
+        private CallIndicatorIconState(int subId) {
             this.subId = subId;
-            this.resId = R.drawable.ic_qs_no_calling_sms;
+            this.noCallingResId = R.drawable.ic_qs_no_calling_sms;
+            this.callStrengthResId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
         }
 
         @Override
@@ -441,27 +455,36 @@
             if (o == null || getClass() != o.getClass()) {
                 return false;
             }
-            NoCallingIconState that = (NoCallingIconState) o;
-            return visible == that.visible
-                    && resId == that.resId
-                    && subId == that.subId;
+            CallIndicatorIconState that = (CallIndicatorIconState) o;
+            return  isNoCalling == that.isNoCalling
+                    && noCallingResId == that.noCallingResId
+                    && callStrengthResId == that.callStrengthResId
+                    && subId == that.subId
+                    && noCallingDescription == that.noCallingDescription
+                    && callStrengthDescription == that.callStrengthDescription;
+
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(visible, resId, subId);
+            return Objects.hash(isNoCalling, noCallingResId,
+                    callStrengthResId, subId, noCallingDescription, callStrengthDescription);
         }
 
-        private void copyTo(NoCallingIconState other) {
-            other.visible = visible;
-            other.resId = resId;
+        private void copyTo(CallIndicatorIconState other) {
+            other.isNoCalling = isNoCalling;
+            other.noCallingResId = noCallingResId;
+            other.callStrengthResId = callStrengthResId;
             other.subId = subId;
+            other.noCallingDescription = noCallingDescription;
+            other.callStrengthDescription = callStrengthDescription;
         }
 
-        private static List<NoCallingIconState> copyStates(List<NoCallingIconState> inStates) {
-            ArrayList<NoCallingIconState> outStates = new ArrayList<>();
-            for (NoCallingIconState state : inStates) {
-                NoCallingIconState copy = new NoCallingIconState(state.subId);
+        private static List<CallIndicatorIconState> copyStates(
+                List<CallIndicatorIconState> inStates) {
+            ArrayList<CallIndicatorIconState> outStates = new ArrayList<>();
+            for (CallIndicatorIconState state : inStates) {
+                CallIndicatorIconState copy = new CallIndicatorIconState(state.subId);
                 state.copyTo(copy);
                 outStates.add(copy);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index b572c57..9e9533d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -47,7 +47,6 @@
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -201,8 +200,7 @@
             DismissCallbackRegistry dismissCallbackRegistry,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             NotificationIconAreaController notificationIconAreaController,
-            BrightnessSlider.Factory brightnessSliderFactory,
-            FeatureFlags featureFlags) {
+            BrightnessSlider.Factory brightnessSliderFactory) {
         return new StatusBar(
                 context,
                 notificationsController,
@@ -281,7 +279,6 @@
                 notificationShadeDepthController,
                 statusBarTouchableRegionManager,
                 notificationIconAreaController,
-                brightnessSliderFactory,
-                featureFlags);
+                brightnessSliderFactory);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index ccaa1f4..5ff8970 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -36,6 +36,7 @@
  * the current or specified Looper.
  */
 public class CallbackHandler extends Handler implements EmergencyListener, SignalCallback {
+    private static final String TAG = "CallbackHandler";
     private static final int MSG_EMERGENCE_CHANGED           = 0;
     private static final int MSG_SUBS_CHANGED                = 1;
     private static final int MSG_NO_SIM_VISIBLE_CHANGED      = 2;
@@ -198,17 +199,17 @@
     }
 
     @Override
-    public void setNoCallingStatus(boolean noCalling, int subId) {
+    public void setCallIndicator(IconState statusIcon, int subId) {
         String log = new StringBuilder()
                 .append(SSDF.format(System.currentTimeMillis())).append(",")
-                .append("setNoCallingStatus: ")
-                .append("noCalling=").append(noCalling).append(",")
+                .append("setCallIndicator: ")
+                .append("statusIcon=").append(statusIcon).append(",")
                 .append("subId=").append(subId)
                 .toString();
         recordLastCallback(log);
         post(() -> {
             for (SignalCallback signalCluster : mSignalCallbacks) {
-                signalCluster.setNoCallingStatus(noCalling, subId);
+                signalCluster.setCallIndicator(statusIcon, subId);
             }
         });
     }
@@ -226,24 +227,11 @@
 
     @Override
     public void setNoSims(boolean show, boolean simDetected) {
-        String log = new StringBuilder()
-                .append(SSDF.format(System.currentTimeMillis())).append(",")
-                .append("setNoSims: ")
-                .append("show=").append(show).append(",")
-                .append("simDetected=").append(simDetected)
-                .toString();
-        recordLastCallback(log);
         obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, simDetected ? 1 : 0).sendToTarget();
     }
 
     @Override
     public void setMobileDataEnabled(boolean enabled) {
-        String log = new StringBuilder()
-                .append(SSDF.format(System.currentTimeMillis())).append(",")
-                .append("setMobileDataEnabled: ")
-                .append("enabled=").append(enabled)
-                .toString();
-        recordLastCallback(log);
         obtainMessage(MSG_MOBILE_DATA_ENABLED_CHANGED, enabled ? 1 : 0, 0).sendToTarget();
     }
 
@@ -283,7 +271,8 @@
     }
 
     protected void recordLastCallback(String callback) {
-        mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)] = callback;
+        mHistory[mHistoryIndex] = callback;
+        mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
     }
 
     /**
@@ -293,7 +282,9 @@
         pw.println("  - CallbackHandler -----");
         int size = 0;
         for (int i = 0; i < HISTORY_SIZE; i++) {
-            if (mHistory[i] != null) size++;
+            if (mHistory[i] != null) {
+                size++;
+            }
         }
         // Print out the previous states in ordered number.
         for (int i = mHistoryIndex + HISTORY_SIZE - 1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 0fe338e..1ab7652 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -26,6 +26,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings.Global;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.CellSignalStrength;
 import android.telephony.CellSignalStrengthCdma;
 import android.telephony.ServiceState;
@@ -34,12 +35,18 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.RegistrationManager.RegistrationCallback;
 import android.text.Html;
 import android.text.TextUtils;
 import android.util.FeatureFlagUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.AccessibilityContentDescriptions;
 import com.android.settingslib.SignalIcon.MobileIconGroup;
 import com.android.settingslib.SignalIcon.MobileState;
 import com.android.settingslib.Utils;
@@ -65,13 +72,19 @@
  */
 public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
     private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
-
+    private static final int STATUS_HISTORY_SIZE = 64;
+    private static final int IMS_TYPE_WWAN = 1;
+    private static final int IMS_TYPE_WLAN = 2;
+    private static final int IMS_TYPE_WLAN_CROSS_SIM = 3;
     private final TelephonyManager mPhone;
+    private final ImsMmTelManager mImsMmTelManager;
     private final SubscriptionDefaults mDefaults;
     private final String mNetworkNameDefault;
     private final String mNetworkNameSeparator;
     private final ContentObserver mObserver;
     private final boolean mProviderModel;
+    private final Handler mReceiverHandler;
+    private int mImsType = IMS_TYPE_WWAN;
     // Save entire info for logging, we only use the id.
     final SubscriptionInfo mSubscriptionInfo;
     // @VisibleForDemoMode
@@ -86,16 +99,21 @@
                     TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
     private ServiceState mServiceState;
     private SignalStrength mSignalStrength;
+    private int mLastLevel;
     private MobileIconGroup mDefaultIcons;
     private Config mConfig;
     @VisibleForTesting
     boolean mInflateSignalStrengths = false;
     private MobileStatusTracker.Callback mCallback;
+    private RegistrationCallback mRegistrationCallback;
+    private int mLastWwanLevel;
+    private int mLastWlanLevel;
+    private int mLastWlanCrossSimLevel;
     @VisibleForTesting
     MobileStatusTracker mMobileStatusTracker;
 
-    // Save the previous HISTORY_SIZE states for logging.
-    private final String[] mMobileStatusHistory = new String[HISTORY_SIZE];
+    // Save the previous STATUS_HISTORY_SIZE states for logging.
+    private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE];
     // Where to copy the next state into.
     private int mMobileStatusHistoryIndex;
 
@@ -116,6 +134,7 @@
                 .toString();
         mNetworkNameDefault = getTextIfExists(
                 com.android.internal.R.string.lockscreen_carrier_default).toString();
+        mReceiverHandler = new Handler(receiverLooper);
 
         mNetworkToIconLookup = mapIconSets(mConfig);
         mDefaultIcons = getDefaultIcons(mConfig);
@@ -133,6 +152,8 @@
             }
         };
         mCallback = new MobileStatusTracker.Callback() {
+            private String mLastStatus;
+
             @Override
             public void onMobileStatusChanged(boolean updateTelephony,
                     MobileStatus mobileStatus) {
@@ -141,11 +162,15 @@
                             + " updateTelephony=" + updateTelephony
                             + " mobileStatus=" + mobileStatus.toString());
                 }
-                String status = new StringBuilder()
-                        .append(SSDF.format(System.currentTimeMillis())).append(",")
-                        .append(mobileStatus.toString())
-                        .toString();
-                recordLastMobileStatus(status);
+                String currentStatus = mobileStatus.toString();
+                if (!currentStatus.equals(mLastStatus)) {
+                    mLastStatus = currentStatus;
+                    String status = new StringBuilder()
+                            .append(SSDF.format(System.currentTimeMillis())).append(",")
+                            .append(currentStatus)
+                            .toString();
+                    recordLastMobileStatus(status);
+                }
                 updateMobileStatus(mobileStatus);
                 if (updateTelephony) {
                     updateTelephony();
@@ -154,6 +179,53 @@
                 }
             }
         };
+
+        mRegistrationCallback = new RegistrationCallback() {
+            @Override
+            public void onRegistered(ImsRegistrationAttributes attributes) {
+                Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
+                int imsTransportType = attributes.getTransportType();
+                int registrationAttributes = attributes.getAttributeFlags();
+                if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+                    mImsType = IMS_TYPE_WWAN;
+                    IconState statusIcon = new IconState(
+                            true,
+                            getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+                            getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+                    notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+                } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+                    if (registrationAttributes == 0) {
+                        mImsType = IMS_TYPE_WLAN;
+                        IconState statusIcon = new IconState(
+                                true,
+                                getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
+                                getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
+                        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+                    } else if (registrationAttributes
+                            == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
+                        mImsType = IMS_TYPE_WLAN_CROSS_SIM;
+                        IconState statusIcon = new IconState(
+                                true,
+                                getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
+                                getCallStrengthDescription(
+                                        mLastWlanCrossSimLevel, /* isWifi= */false));
+                        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+                    }
+                }
+            }
+
+            @Override
+            public void onUnregistered(ImsReasonInfo info) {
+                Log.d(mTag, "onDeregistered: " + "info=" + info);
+                mImsType = IMS_TYPE_WWAN;
+                IconState statusIcon = new IconState(
+                        true,
+                        getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+                        getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+                notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+            }
+        };
+        mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
         mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
                 info, mDefaults, mCallback);
         mProviderModel = FeatureFlagUtils.isEnabled(
@@ -202,14 +274,41 @@
         mContext.getContentResolver().registerContentObserver(Global.getUriFor(
                 Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),
                 true, mObserver);
+        if (mProviderModel) {
+            mReceiverHandler.post(mTryRegisterIms);
+        }
     }
 
+    // There is no listener to monitor whether the IMS service is ready, so we have to retry the
+    // IMS registration.
+    private final Runnable mTryRegisterIms = new Runnable() {
+        private static final int MAX_RETRY = 12;
+        private int mRetryCount;
+
+        @Override
+        public void run() {
+            try {
+                mRetryCount++;
+                mImsMmTelManager.registerImsRegistrationCallback(
+                        mReceiverHandler::post, mRegistrationCallback);
+                Log.d(mTag, "registerImsRegistrationCallback succeeded");
+            } catch (RuntimeException | ImsException e) {
+                if (mRetryCount < MAX_RETRY) {
+                    Log.e(mTag, mRetryCount + " registerImsRegistrationCallback failed", e);
+                    // Wait for 5 seconds to retry
+                    mReceiverHandler.postDelayed(mTryRegisterIms, 5000);
+                }
+            }
+        }
+    };
+
     /**
      * Stop listening for phone state changes.
      */
     public void unregisterListener() {
         mMobileStatusTracker.setListening(false);
         mContext.getContentResolver().unregisterContentObserver(mObserver);
+        mImsMmTelManager.unregisterImsRegistrationCallback(mRegistrationCallback);
     }
 
     private void updateInflateSignalStrength() {
@@ -452,9 +551,9 @@
     /**
      * Extracts the CellSignalStrengthCdma from SignalStrength then returns the level
      */
-    private final int getCdmaLevel() {
+    private int getCdmaLevel(SignalStrength signalStrength) {
         List<CellSignalStrengthCdma> signalStrengthCdma =
-            mSignalStrength.getCellSignalStrengths(CellSignalStrengthCdma.class);
+                signalStrength.getCellSignalStrengths(CellSignalStrengthCdma.class);
         if (!signalStrengthCdma.isEmpty()) {
             return signalStrengthCdma.get(0).getLevel();
         }
@@ -467,6 +566,7 @@
         mCurrentState.dataSim = mobileStatus.dataSim;
         mCurrentState.carrierNetworkChangeMode = mobileStatus.carrierNetworkChangeMode;
         mDataState = mobileStatus.dataState;
+        notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength);
         mSignalStrength = mobileStatus.signalStrength;
         mTelephonyDisplayInfo = mobileStatus.telephonyDisplayInfo;
         int lastVoiceState = mServiceState != null ? mServiceState.getState() : -1;
@@ -481,9 +581,117 @@
                 && (lastVoiceState == -1
                         || (lastVoiceState == ServiceState.STATE_IN_SERVICE
                                 || currentVoiceState == ServiceState.STATE_IN_SERVICE))) {
-            notifyNoCallingStatusChange(
-                    currentVoiceState != ServiceState.STATE_IN_SERVICE,
-                    mSubscriptionInfo.getSubscriptionId());
+            boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
+            IconState statusIcon = new IconState(isNoCalling, R.drawable.ic_qs_no_calling_sms,
+                    getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
+            notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+        }
+    }
+
+    private int getCallStrengthIcon(int level, boolean isWifi) {
+        return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level]
+                : TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level];
+    }
+
+    private String getCallStrengthDescription(int level, boolean isWifi) {
+        return isWifi
+                ? getTextIfExists(AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[level])
+                        .toString()
+                : getTextIfExists(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[level])
+                        .toString();
+    }
+
+    void refreshCallIndicator(SignalCallback callback) {
+        boolean isNoCalling = mServiceState != null
+                && mServiceState.getState() != ServiceState.STATE_IN_SERVICE;
+        IconState statusIcon = new IconState(isNoCalling, R.drawable.ic_qs_no_calling_sms,
+                getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
+        callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
+
+        switch (mImsType) {
+            case IMS_TYPE_WWAN:
+                statusIcon = new IconState(
+                        true,
+                        getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+                        getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+                break;
+            case IMS_TYPE_WLAN:
+                statusIcon = new IconState(
+                        true,
+                        getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
+                        getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
+                break;
+            case IMS_TYPE_WLAN_CROSS_SIM:
+                statusIcon = new IconState(
+                        true,
+                        getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
+                        getCallStrengthDescription(mLastWlanCrossSimLevel, /* isWifi= */false));
+        }
+        callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
+    }
+
+    void notifyWifiLevelChange(int level) {
+        if (!mProviderModel) {
+            return;
+        }
+        Log.d("mTag", "notifyWifiLevelChange " + mImsType);
+        mLastWlanLevel = level;
+        if (mImsType != IMS_TYPE_WLAN) {
+            return;
+        }
+        IconState statusIcon = new IconState(
+                true,
+                getCallStrengthIcon(level, /* isWifi= */true),
+                getCallStrengthDescription(level, /* isWifi= */true));
+        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+    }
+
+    void notifyDefaultMobileLevelChange(int level) {
+        if (!mProviderModel) {
+            return;
+        }
+        Log.d("mTag", "notifyDefaultMobileLevelChange " + mImsType);
+        mLastWlanCrossSimLevel = level;
+        if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) {
+            return;
+        }
+        IconState statusIcon = new IconState(
+                true,
+                getCallStrengthIcon(level, /* isWifi= */false),
+                getCallStrengthDescription(level, /* isWifi= */false));
+        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+    }
+
+    void notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength) {
+        if (!mProviderModel) {
+            return;
+        }
+        int newLevel = getSignalLevel(signalStrength);
+        if (newLevel != mLastLevel) {
+            mLastLevel = newLevel;
+            Log.d("mTag", "notifyMobileLevelChangeIfNecessary " + mImsType);
+            mLastWwanLevel = newLevel;
+            if (mImsType == IMS_TYPE_WWAN) {
+                IconState statusIcon = new IconState(
+                        true,
+                        getCallStrengthIcon(newLevel, /* isWifi= */false),
+                        getCallStrengthDescription(newLevel, /* isWifi= */false));
+                notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+            }
+            if (mCurrentState.dataSim) {
+                mNetworkController.notifyDefaultMobileLevelChange(newLevel);
+            }
+        }
+    }
+
+    int getSignalLevel(SignalStrength signalStrength) {
+        if (signalStrength == null) {
+            return 0;
+        }
+        if (!signalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
+            return getCdmaLevel(signalStrength);
+        } else {
+            return signalStrength.getLevel();
         }
     }
 
@@ -501,11 +709,7 @@
         checkDefaultData();
         mCurrentState.connected = Utils.isInService(mServiceState) && mSignalStrength != null;
         if (mCurrentState.connected) {
-            if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
-                mCurrentState.level = getCdmaLevel();
-            } else {
-                mCurrentState.level = mSignalStrength.getLevel();
-            }
+            mCurrentState.level = getSignalLevel(mSignalStrength);
         }
 
         String iconKey = getIconKey(mTelephonyDisplayInfo);
@@ -577,7 +781,13 @@
     }
 
     private void recordLastMobileStatus(String mobileStatus) {
-        mMobileStatusHistory[mMobileStatusHistoryIndex++ & (HISTORY_SIZE - 1)] = mobileStatus;
+        mMobileStatusHistory[mMobileStatusHistoryIndex] = mobileStatus;
+        mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE;
+    }
+
+    @VisibleForTesting
+    void setImsType(int imsType) {
+        mImsType = imsType;
     }
 
     @Override
@@ -592,15 +802,17 @@
         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
         pw.println("  MobileStatusHistory");
         int size = 0;
-        for (int i = 0; i < HISTORY_SIZE; i++) {
-            if (mMobileStatusHistory[i] != null) size++;
+        for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
+            if (mMobileStatusHistory[i] != null) {
+                size++;
+            }
         }
         // Print out the previous states in ordered number.
-        for (int i = mMobileStatusHistoryIndex + HISTORY_SIZE - 1;
-                i >= mMobileStatusHistoryIndex + HISTORY_SIZE - size; i--) {
+        for (int i = mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - 1;
+                i >= mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - size; i--) {
             pw.println("  Previous MobileStatus("
-                    + (mMobileStatusHistoryIndex + HISTORY_SIZE - i) + "): "
-                    + mMobileStatusHistory[i & (HISTORY_SIZE - 1)]);
+                    + (mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - i) + "): "
+                    + mMobileStatusHistory[i & (STATUS_HISTORY_SIZE - 1)]);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index e60d5c5..0a9fead 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -53,7 +53,7 @@
 
         /**
          * Callback for listeners to be able to update the state of any UI tracking connectivity
-         *  @param statusIcon the icon that should be shown in the status bar
+         * @param statusIcon the icon that should be shown in the status bar
          * @param qsIcon the icon to show in Quick Settings
          * @param statusType the resId of the data type icon (e.g. LTE) to show in the status bar
          * @param qsType similar to above, the resId of the data type icon to show in Quick Settings
@@ -95,11 +95,11 @@
                 boolean noNetworksAvailable) {}
 
         /**
-         * Callback for listeners to be able to update the no calling & SMS status
-         * @param noCalling whether the calling and SMS is not working.
+         * Callback for listeners to be able to update the call indicator
+         * @param statusIcon the icon for the call indicator
          * @param subId subscription ID for which to update the UI
          */
-        default void setNoCallingStatus(boolean noCalling, int subId) {}
+        default void setCallIndicator(IconState statusIcon, int subId) {}
     }
 
     public interface EmergencyListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 80c7811..9f92142 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -554,6 +554,20 @@
         return controller != null ? controller.getNetworkNameForCarrierWiFi() : "";
     }
 
+    void notifyWifiLevelChange(int level) {
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
+            mobileSignalController.notifyWifiLevelChange(level);
+        }
+    }
+
+    void notifyDefaultMobileLevelChange(int level) {
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
+            mobileSignalController.notifyDefaultMobileLevelChange(level);
+        }
+    }
+
     private void notifyControllersMobileDataChanged() {
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -623,6 +637,9 @@
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
             mobileSignalController.notifyListeners(cb);
+            if (mProviderModel) {
+                mobileSignalController.refreshCallIndicator(cb);
+            }
         }
         mCallbackHandler.setListening(cb, true);
     }
@@ -1272,7 +1289,8 @@
     }
 
     private void recordLastNetworkCallback(String callback) {
-        mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)] = callback;
+        mHistory[mHistoryIndex] = callback;
+        mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
     }
 
     private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
index 554145e..4b6722c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
@@ -23,6 +23,7 @@
 
 import com.android.settingslib.SignalIcon.IconGroup;
 import com.android.settingslib.SignalIcon.State;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
 import java.io.PrintWriter;
@@ -167,8 +168,8 @@
         }
     }
 
-    protected final void notifyNoCallingStatusChange(boolean noCalling, int subId) {
-        mCallbackHandler.setNoCallingStatus(noCalling, subId);
+    protected final void notifyCallStateChange(IconState statusIcon, int subId) {
+        mCallbackHandler.setCallIndicator(statusIcon, subId);
     }
 
     /**
@@ -187,7 +188,8 @@
      * and last value of any state data.
      */
     protected void recordLastState() {
-        mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)].copyFrom(mLastState);
+        mHistory[mHistoryIndex].copyFrom(mLastState);
+        mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
     }
 
     public void dump(PrintWriter pw) {
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 1838391..019ab47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -680,11 +680,7 @@
             if (item.isAddUser) {
                 iconRes = R.drawable.ic_add_circle;
             } else if (item.isGuest) {
-                if (item.isCurrent) {
-                    iconRes = R.drawable.ic_exit_to_app;
-                } else {
-                    iconRes = R.drawable.ic_avatar_guest_user;
-                }
+                iconRes = R.drawable.ic_avatar_guest_user;
             } else {
                 iconRes = R.drawable.ic_avatar_user;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 1fd2ccb..47207d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -202,6 +202,7 @@
         mCurrentState.connected = mWifiTracker.connected;
         mCurrentState.ssid = mWifiTracker.ssid;
         mCurrentState.rssi = mWifiTracker.rssi;
+        notifyWifiLevelChangeIfNecessary(mWifiTracker.level);
         mCurrentState.level = mWifiTracker.level;
         mCurrentState.statusLabel = mWifiTracker.statusLabel;
         mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged;
@@ -211,6 +212,12 @@
                         : mUnmergedWifiIconGroup;
     }
 
+    void notifyWifiLevelChangeIfNecessary(int level) {
+        if (level != mCurrentState.level) {
+            mNetworkController.notifyWifiLevelChange(level);
+        }
+    }
+
     boolean isCarrierMergedWifi(int subId) {
         return mCurrentState.isDefault
                 && mCurrentState.isCarrierMerged && (mCurrentState.subId == subId);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 09335af..b67574d 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -109,7 +109,7 @@
         dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.cancel),
                 (OnClickListener) null);
         dialog.setButton(DialogInterface.BUTTON_POSITIVE,
-                context.getString(R.string.qs_customize_remove), new OnClickListener() {
+                context.getString(R.string.guest_exit_guest_dialog_remove), new OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
                         // Tell the tuner (in main SysUI process) to clear all its settings.
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 6e7aed0..afeda967 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -45,6 +45,7 @@
 import android.service.notification.ZenModeConfig;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.view.View;
 
 import androidx.annotation.NonNull;
@@ -86,10 +87,12 @@
 import java.io.PrintWriter;
 import java.lang.reflect.Array;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 import java.util.function.IntConsumer;
 import java.util.function.Supplier;
 
@@ -248,38 +251,19 @@
                 });
 
         mSysuiProxy = new Bubbles.SysuiProxy() {
-            private <T> T executeBlockingForResult(Supplier<T> runnable, Executor executor,
-                    Class clazz) {
-                if (Looper.myLooper() == Looper.getMainLooper()) {
-                    return runnable.get();
-                }
-                final T[] result = (T[]) Array.newInstance(clazz, 1);
-                final CountDownLatch latch = new CountDownLatch(1);
-                executor.execute(() -> {
-                    result[0] = runnable.get();
-                    latch.countDown();
-                });
-                try {
-                    latch.await();
-                    return result[0];
-                } catch (InterruptedException e) {
-                    return null;
-                }
-            }
-
             @Override
-            @Nullable
-            public BubbleEntry getPendingOrActiveEntry(String key) {
-                return executeBlockingForResult(() -> {
+            public void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback) {
+                sysuiMainExecutor.execute(() -> {
                     NotificationEntry entry =
                             mNotificationEntryManager.getPendingOrActiveNotif(key);
-                    return entry == null ? null : notifToBubbleEntry(entry);
-                }, sysuiMainExecutor, BubbleEntry.class);
+                    callback.accept(entry == null ? null : notifToBubbleEntry(entry));
+                });
             }
 
             @Override
-            public List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys) {
-                return executeBlockingForResult(() -> {
+            public void getShouldRestoredEntries(ArraySet<String> savedBubbleKeys,
+                    Consumer<List<BubbleEntry>> callback) {
+                sysuiMainExecutor.execute(() -> {
                     List<BubbleEntry> result = new ArrayList<>();
                     List<NotificationEntry> activeEntries =
                             mNotificationEntryManager.getActiveNotificationsForCurrentUser();
@@ -291,27 +275,8 @@
                             result.add(notifToBubbleEntry(entry));
                         }
                     }
-                    return result;
-                }, sysuiMainExecutor, List.class);
-            }
-
-            @Override
-            public boolean isNotificationShadeExpand() {
-                return executeBlockingForResult(() -> {
-                    return mNotificationShadeWindowController.getPanelExpanded();
-                }, sysuiMainExecutor, Boolean.class);
-            }
-
-            @Override
-            public boolean shouldBubbleUp(String key) {
-                return executeBlockingForResult(() -> {
-                    final NotificationEntry entry =
-                            mNotificationEntryManager.getPendingOrActiveNotif(key);
-                    if (entry != null) {
-                        return mNotificationInterruptStateProvider.shouldBubbleUp(entry);
-                    }
-                    return false;
-                }, sysuiMainExecutor, Boolean.class);
+                    callback.accept(result);
+                });
             }
 
             @Override
@@ -587,7 +552,20 @@
     }
 
     void onRankingUpdate(RankingMap rankingMap) {
-        mBubbles.onRankingUpdated(rankingMap);
+        String[] orderedKeys = rankingMap.getOrderedKeys();
+        HashMap<String, Pair<BubbleEntry, Boolean>> pendingOrActiveNotif = new HashMap<>();
+        for (int i = 0; i < orderedKeys.length; i++) {
+            String key = orderedKeys[i];
+            NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
+            BubbleEntry bubbleEntry = entry != null
+                    ? notifToBubbleEntry(entry)
+                    : null;
+            boolean shouldBubbleUp = entry != null
+                    ? mNotificationInterruptStateProvider.shouldBubbleUp(entry)
+                    : false;
+            pendingOrActiveNotif.put(key, new Pair<>(bubbleEntry, shouldBubbleUp));
+        }
+        mBubbles.onRankingUpdated(rankingMap, pendingOrActiveNotif);
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java
index a52a598..0457100 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java
@@ -19,7 +19,7 @@
 import android.app.Activity;
 import android.os.Bundle;
 
-import com.android.systemui.R;
+import com.android.systemui.tests.R;
 
 /**
  * Test activity for resolving {@link EmergencyGesture#ACTION_LAUNCH_EMERGENCY} action.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index b452d3a..1ec1da4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -98,7 +98,7 @@
 
         mQSCarrierGroupController = new QSCarrierGroupController.Builder(
                 mActivityStarter, handler, TestableLooper.get(this).getLooper(),
-                mNetworkController, mCarrierTextControllerBuilder)
+                mNetworkController, mCarrierTextControllerBuilder, mContext)
                 .setQSCarrierGroup(mQSCarrierGroup)
                 .build();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
index bd37259..4c84df2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
@@ -34,7 +34,7 @@
         linearLayout.setOrientation(LinearLayout.VERTICAL);
         TextView text = new TextView(this);
         text.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 40);
-        text.setText(com.android.systemui.R.string.test_content);
+        text.setText(com.android.systemui.tests.R.string.test_content);
         linearLayout.addView(text);
         scrollView.addView(linearLayout);
         setContentView(scrollView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
index d131dce..e16d4d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
@@ -36,16 +36,20 @@
 
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.os.Handler;
 import android.os.UserHandle;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.util.Pair;
 
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.DeviceConfigProxyFake;
 
 import junit.framework.Assert;
 
@@ -55,38 +59,44 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class AssistantFeedbackControllerTest extends SysuiTestCase {
-    private static final int ON = 1;
-    private static final int OFF = 0;
     private static final String TEST_PACKAGE_NAME = "test_package";
     private static final int TEST_UID = 1;
 
     private AssistantFeedbackController mAssistantFeedbackController;
+    private DeviceConfigProxyFake mProxyFake;
+    private TestableLooper mTestableLooper;
+
     private StatusBarNotification mSbn;
 
     @Before
     public void setUp() {
-        mAssistantFeedbackController = new AssistantFeedbackController(mContext);
-        switchSetting(ON);
+        mProxyFake = new DeviceConfigProxyFake();
+        mTestableLooper = TestableLooper.get(this);
+        mAssistantFeedbackController = new AssistantFeedbackController(
+                new Handler(mTestableLooper.getLooper()),
+                mContext, mProxyFake);
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
                 0, null, TEST_UID, 0, new Notification(),
                 UserHandle.CURRENT, null, 0);
     }
 
     @Test
-    public void testUserControls_settingDisabled() {
-        switchSetting(OFF);
+    public void testFlagDisabled() {
+        switchFlag("false");
         assertFalse(mAssistantFeedbackController.isFeedbackEnabled());
     }
 
     @Test
-    public void testUserControls_settingEnabled() {
+    public void testFlagEnabled() {
+        switchFlag("true");
         assertTrue(mAssistantFeedbackController.isFeedbackEnabled());
     }
 
     @Test
-    public void testFeedback_settingDisabled() {
-        switchSetting(OFF);
+    public void testFeedback_flagDisabled() {
+        switchFlag("false");
         assertEquals(STATUS_UNCHANGED, mAssistantFeedbackController.getFeedbackStatus(
                 getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
         assertFalse(mAssistantFeedbackController.showFeedbackIndicator(
@@ -95,6 +105,7 @@
 
     @Test
     public void testFeedback_changedImportance() {
+        switchFlag("true");
         NotificationEntry entry = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_UNCHANGED);
         assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
         assertTrue(mAssistantFeedbackController.showFeedbackIndicator(entry));
@@ -110,6 +121,7 @@
 
     @Test
     public void testFeedback_changedRanking() {
+        switchFlag("true");
         NotificationEntry entry =
                 getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_PROMOTED);
         assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
@@ -121,8 +133,8 @@
     }
 
     @Test
-    public void testGetFeedbackResources_settingDisabled() {
-        switchSetting(OFF);
+    public void testGetFeedbackResources_flagDisabled() {
+        switchFlag("false");
         Assert.assertEquals(new Pair(0, 0), mAssistantFeedbackController.getFeedbackResources(
                 getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
     }
@@ -138,9 +150,10 @@
                 .build();
     }
 
-    private void switchSetting(int setting) {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.NOTIFICATION_FEEDBACK_ENABLED, setting);
-        mAssistantFeedbackController.update(null);
+    private void switchFlag(String enabled) {
+        mProxyFake.setProperty(
+                DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.ENABLE_NAS_FEEDBACK,
+                enabled, false);
+        mTestableLooper.processAllMessages();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
index 7eeae67..e6287e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
@@ -38,8 +38,8 @@
 import androidx.palette.graphics.Palette;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.tests.R;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 6b0a23f..2e2945e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -50,7 +50,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.filters.Suppress;
 
-import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.media.MediaFeatureFlag;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -61,6 +60,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.policy.InflatedSmartReplies;
 import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflater;
+import com.android.systemui.tests.R;
 
 import org.junit.Assert;
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 6fcc7fa..64a7bee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -43,7 +43,6 @@
 import android.view.LayoutInflater;
 import android.widget.RemoteViews;
 
-import com.android.systemui.R;
 import com.android.systemui.TestableDependency;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.media.MediaFeatureFlag;
@@ -68,6 +67,7 @@
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.InflatedSmartReplies;
+import com.android.systemui.tests.R;
 import com.android.systemui.wmshell.BubblesManager;
 import com.android.systemui.wmshell.BubblesTestActivity;
 import com.android.wm.shell.bubbles.Bubbles;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index a147c8d..45f7c5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -24,10 +24,10 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.tests.R;
 
 import org.junit.Assert;
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index b9fd75e..fa253e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -35,7 +35,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.tuner.TunerService;
 
@@ -58,7 +57,6 @@
     @Mock private PowerManager mPowerManager;
     @Mock private TunerService mTunerService;
     @Mock private BatteryController mBatteryController;
-    @Mock private FeatureFlags mFeatureFlags;
 
     @Before
     public void setup() {
@@ -69,8 +67,7 @@
             mAlwaysOnDisplayPolicy,
             mPowerManager,
             mBatteryController,
-            mTunerService,
-            mFeatureFlags
+            mTunerService
         );
     }
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 253460d..cae488a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -97,7 +97,6 @@
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -259,7 +258,6 @@
     @Mock private DemoModeController mDemoModeController;
     @Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
     @Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
-    @Mock private FeatureFlags mFeatureFlags;
     private ShadeController mShadeController;
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
     private InitController mInitController = new InitController();
@@ -420,8 +418,7 @@
                 mNotificationShadeDepthControllerLazy,
                 mStatusBarTouchableRegionManager,
                 mNotificationIconAreaController,
-                mBrightnessSliderFactory,
-                mFeatureFlags);
+                mBrightnessSliderFactory);
 
         when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
                 mLockIconContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index c212cf3..67c1a08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -26,12 +26,12 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.settingslib.R;
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.tests.R;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index f8b6383..89cc2b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -430,6 +430,10 @@
         updateSignalStrength();
     }
 
+    public void setImsType(int imsType) {
+        mMobileSignalController.setImsType(imsType);
+    }
+
     public void setIsGsm(boolean gsm) {
         when(mSignalStrength.isGsm()).thenReturn(gsm);
         updateSignalStrength();
@@ -632,6 +636,14 @@
         }
     }
 
+    protected void verifyLastCallStrength(int icon) {
+        ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
+        verify(mCallbackHandler, Mockito.atLeastOnce()).setCallIndicator(
+                iconArg.capture(),
+                anyInt());
+        assertEquals("Call strength, in status bar", icon, (int) iconArg.getValue().icon);
+    }
+
     protected void assertNetworkNameEquals(String expected) {
        assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 10166cb..fc1a08ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -15,6 +15,7 @@
 import android.net.vcn.VcnTransportInfo;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.telephony.CellSignalStrength;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -243,6 +244,28 @@
         }
     }
 
+    @Test
+    public void testCallStrengh() {
+        String testSsid = "Test SSID";
+        setWifiEnabled(true);
+        setWifiState(true, testSsid);
+        // Set the ImsType to be IMS_TYPE_WLAN
+        setImsType(2);
+        setWifiLevel(1);
+        for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
+            setWifiLevel(testLevel);
+            verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[testLevel]);
+        }
+        // Set the ImsType to be IMS_TYPE_WWAN
+        setImsType(1);
+        for (int testStrength = 0;
+                testStrength < CellSignalStrength.getNumSignalStrengthLevels(); testStrength++) {
+            setupDefaultSignal();
+            setLevel(testStrength);
+            verifyLastCallStrength(TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[testStrength]);
+        }
+    }
+
     protected void setWifiActivity(int activity) {
         // TODO: Not this, because this variable probably isn't sticking around.
         mNetworkController.mWifiSignalController.setActivity(activity);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index c0af15b..203ece9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -19,8 +19,8 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 
 import java.util.List;
@@ -66,7 +66,7 @@
     }
 
     @Override
-    public void setNoCallingIcons(String slot, List<NoCallingIconState> states) {
+    public void setCallIndicatorIcons(String slot, List<CallIndicatorIconState> states) {
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
index f4d96a12..ab329c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
@@ -20,7 +20,7 @@
 import android.content.Intent;
 import android.os.Bundle;
 
-import com.android.systemui.R;
+import com.android.systemui.tests.R;
 
 /**
  * Referenced by NotificationTestHelper#makeBubbleMetadata
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 38275f7..6c30999 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1245,9 +1245,10 @@
 
     @Override
     public IRestoreSession beginRestoreSessionForUser(
-            int userId, String packageName, String transportID) throws RemoteException {
+            int userId, String packageName, String transportID,
+            @OperationType int operationType) throws RemoteException {
         return isUserReadyForBackup(userId)
-                ? beginRestoreSession(userId, packageName, transportID) : null;
+                ? beginRestoreSession(userId, packageName, transportID, operationType) : null;
     }
 
     /**
@@ -1256,13 +1257,15 @@
      */
     @Nullable
     public IRestoreSession beginRestoreSession(
-            @UserIdInt int userId, String packageName, String transportName) {
+            @UserIdInt int userId, String packageName, String transportName,
+            @OperationType int operationType) {
         UserBackupManagerService userBackupManagerService =
                 getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
 
         return userBackupManagerService == null
                 ? null
-                : userBackupManagerService.beginRestoreSession(packageName, transportName);
+                : userBackupManagerService.beginRestoreSession(packageName, transportName,
+                        operationType);
     }
 
     @Override
@@ -1347,15 +1350,15 @@
         if (!isUserReadyForBackup(userId)) {
             return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
         }
-        return requestBackup(userId, packages, observer, monitor, flags);
+        return requestBackup(userId, packages, observer, monitor, flags, OperationType.BACKUP);
     }
 
     @Override
     public int requestBackup(String[] packages, IBackupObserver observer,
-            IBackupManagerMonitor monitor, int flags)
+            IBackupManagerMonitor monitor, int flags, @OperationType int operationType)
             throws RemoteException {
         return requestBackup(binderGetCallingUserId(), packages,
-                observer, monitor, flags);
+                observer, monitor, flags, operationType);
     }
 
     /**
@@ -1367,13 +1370,15 @@
             String[] packages,
             IBackupObserver observer,
             IBackupManagerMonitor monitor,
-            int flags) {
+            int flags,
+            @OperationType int operationType) {
         UserBackupManagerService userBackupManagerService =
                 getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
 
         return userBackupManagerService == null
                 ? BackupManager.ERROR_BACKUP_NOT_ALLOWED
-                : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
+                : userBackupManagerService.requestBackup(packages, observer, monitor, flags,
+                        operationType);
     }
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index faec95f..136cd22f 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -127,7 +127,6 @@
 import com.android.server.backup.restore.ActiveRestoreSession;
 import com.android.server.backup.restore.PerformUnifiedRestoreTask;
 import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.transport.TransportNotAvailableException;
 import com.android.server.backup.transport.TransportNotRegisteredException;
 import com.android.server.backup.utils.BackupEligibilityRules;
 import com.android.server.backup.utils.BackupManagerMonitorUtils;
@@ -1861,10 +1860,19 @@
 
     /**
      * Requests a backup for the inputted {@code packages} with a specified {@link
-     * IBackupManagerMonitor} and {@link OperationType}.
+     * IBackupManagerMonitor}.
      */
     public int requestBackup(String[] packages, IBackupObserver observer,
             IBackupManagerMonitor monitor, int flags) {
+        return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP);
+    }
+
+    /**
+     * Requests a backup for the inputted {@code packages} with a specified {@link
+     * IBackupManagerMonitor} and {@link OperationType}.
+     */
+    public int requestBackup(String[] packages, IBackupObserver observer,
+            IBackupManagerMonitor monitor, int flags, @OperationType int operationType) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
 
         if (packages == null || packages.length < 1) {
@@ -1895,16 +1903,13 @@
 
         final TransportClient transportClient;
         final String transportDirName;
-        int operationType;
         try {
             transportDirName =
                     mTransportManager.getTransportDirName(
                             mTransportManager.getCurrentTransportName());
             transportClient =
                     mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
-            operationType = getOperationTypeFromTransport(transportClient);
-        } catch (TransportNotRegisteredException | TransportNotAvailableException
-                | RemoteException e) {
+        } catch (TransportNotRegisteredException e) {
             BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
             monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
                     BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
@@ -4019,13 +4024,15 @@
     }
 
     /** Hand off a restore session. */
-    public IRestoreSession beginRestoreSession(String packageName, String transport) {
+    public IRestoreSession beginRestoreSession(String packageName, String transport,
+            @OperationType int operationType) {
         if (DEBUG) {
             Slog.v(
                     TAG,
                     addUserIdToLogMessage(
                             mUserId,
-                            "beginRestoreSession: pkg=" + packageName + " transport=" + transport));
+                            "beginRestoreSession: pkg=" + packageName + " transport=" + transport
+                                + "operationType=" + operationType));
         }
 
         boolean needPermission = true;
@@ -4066,17 +4073,6 @@
             }
         }
 
-        int operationType;
-        try {
-            operationType = getOperationTypeFromTransport(
-                    mTransportManager.getTransportClientOrThrow(transport, /* caller */
-                            "BMS.beginRestoreSession"));
-        } catch (TransportNotAvailableException | TransportNotRegisteredException
-                | RemoteException e) {
-            Slog.w(TAG, "Failed to get operation type from transport: " + e);
-            return null;
-        }
-
         synchronized (this) {
             if (mActiveRestoreSession != null) {
                 Slog.i(
@@ -4360,23 +4356,6 @@
         }
     }
 
-    @VisibleForTesting
-    @OperationType int getOperationTypeFromTransport(TransportClient transportClient)
-            throws TransportNotAvailableException, RemoteException {
-        long oldCallingId = Binder.clearCallingIdentity();
-        try {
-            IBackupTransport transport = transportClient.connectOrThrow(
-                    /* caller */ "BMS.getOperationTypeFromTransport");
-            if ((transport.getTransportFlags() & BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER) != 0) {
-                return OperationType.MIGRATION;
-            } else {
-                return OperationType.BACKUP;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(oldCallingId);
-        }
-    }
-
     private static String addUserIdToLogMessage(int userId, String message) {
         return "[UserID:" + userId + "] " + message;
     }
diff --git a/services/core/java/com/android/server/accounts/OWNERS b/services/core/java/com/android/server/accounts/OWNERS
index ea5fd36..8dcc04a 100644
--- a/services/core/java/com/android/server/accounts/OWNERS
+++ b/services/core/java/com/android/server/accounts/OWNERS
@@ -3,7 +3,6 @@
 sandrakwan@google.com
 hackbod@google.com
 svetoslavganov@google.com
-moltmann@google.com
 fkupolov@google.com
 yamasani@google.com
 omakoto@google.com
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f0f29a9..bedd19b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1490,7 +1490,9 @@
     private static final int INDEX_TOTAL_SWAP_PSS = 10;
     private static final int INDEX_TOTAL_RSS = 11;
     private static final int INDEX_TOTAL_NATIVE_PSS = 12;
-    private static final int INDEX_LAST = 13;
+    private static final int INDEX_TOTAL_MEMTRACK_GRAPHICS = 13;
+    private static final int INDEX_TOTAL_MEMTRACK_GL = 14;
+    private static final int INDEX_LAST = 15;
 
     final class UiHandler extends Handler {
         public UiHandler() {
@@ -10316,6 +10318,7 @@
         long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
         long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
         long[] miscRss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
+        long[] memtrackTmp = new long[4];
 
         long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
         long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length];
@@ -10349,6 +10352,8 @@
                 final int reportType;
                 final long startTime;
                 final long endTime;
+                long memtrackGraphics = 0;
+                long memtrackGl = 0;
                 if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
                     reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW;
                     startTime = SystemClock.currentThreadTimeMillis();
@@ -10360,7 +10365,7 @@
                 } else {
                     reportType = ProcessStats.ADD_PSS_EXTERNAL;
                     startTime = SystemClock.currentThreadTimeMillis();
-                    long pss = Debug.getPss(pid, tmpLong, null);
+                    long pss = Debug.getPss(pid, tmpLong, memtrackTmp);
                     if (pss == 0) {
                         continue;
                     }
@@ -10368,6 +10373,8 @@
                     endTime = SystemClock.currentThreadTimeMillis();
                     mi.dalvikPrivateDirty = (int) tmpLong[0];
                     mi.dalvikRss = (int) tmpLong[2];
+                    memtrackGraphics = memtrackTmp[1];
+                    memtrackGl = memtrackTmp[2];
                 }
                 if (!opts.isCheckinRequest && opts.dumpDetails) {
                     pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **");
@@ -10431,6 +10438,8 @@
                     ss[INDEX_TOTAL_PSS] += myTotalPss;
                     ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
                     ss[INDEX_TOTAL_RSS] += myTotalRss;
+                    ss[INDEX_TOTAL_MEMTRACK_GRAPHICS] += memtrackGraphics;
+                    ss[INDEX_TOTAL_MEMTRACK_GL] += memtrackGl;
                     MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
                             (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
                             myTotalSwapPss, myTotalRss, pid, hasActivities);
@@ -10494,6 +10503,8 @@
             final Debug.MemoryInfo[] memInfos = new Debug.MemoryInfo[1];
             mAppProfiler.forAllCpuStats((st) -> {
                 if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
+                    long memtrackGraphics = 0;
+                    long memtrackGl = 0;
                     if (memInfos[0] == null) {
                         memInfos[0] = new Debug.MemoryInfo();
                     }
@@ -10503,13 +10514,15 @@
                             return;
                         }
                     } else {
-                        long pss = Debug.getPss(st.pid, tmpLong, null);
+                        long pss = Debug.getPss(st.pid, tmpLong, memtrackTmp);
                         if (pss == 0) {
                             return;
                         }
                         info.nativePss = (int) pss;
                         info.nativePrivateDirty = (int) tmpLong[0];
                         info.nativeRss = (int) tmpLong[2];
+                        memtrackGraphics = memtrackTmp[1];
+                        memtrackGl = memtrackTmp[2];
                     }
 
                     final long myTotalPss = info.getTotalPss();
@@ -10519,6 +10532,8 @@
                     ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
                     ss[INDEX_TOTAL_RSS] += myTotalRss;
                     ss[INDEX_TOTAL_NATIVE_PSS] += myTotalPss;
+                    ss[INDEX_TOTAL_MEMTRACK_GRAPHICS] += memtrackGraphics;
+                    ss[INDEX_TOTAL_MEMTRACK_GL] += memtrackGl;
 
                     MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
                             st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss,
@@ -10726,7 +10741,11 @@
                     pw.print(" mapped + ");
                     pw.print(stringifyKBSize(dmabufUnmapped));
                     pw.println(" unmapped)");
-                    kernelUsed += totalExportedDmabuf;
+                    // Account unmapped dmabufs as part of kernel memory allocations
+                    kernelUsed += dmabufUnmapped;
+                    // Replace memtrack HAL reported Graphics category with mapped dmabufs
+                    ss[INDEX_TOTAL_PSS] -= ss[INDEX_TOTAL_MEMTRACK_GRAPHICS];
+                    ss[INDEX_TOTAL_PSS] += dmabufMapped;
                 }
                 final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb();
                 if (totalDmabufHeapPool >= 0) {
@@ -10736,13 +10755,27 @@
             }
             final long gpuUsage = Debug.getGpuTotalUsageKb();
             if (gpuUsage >= 0) {
-                pw.print("      GPU: "); pw.println(stringifyKBSize(gpuUsage));
+                final long gpuDmaBufUsage = Debug.getGpuDmaBufUsageKb();
+                if (gpuDmaBufUsage >= 0) {
+                    final long gpuPrivateUsage = gpuUsage - gpuDmaBufUsage;
+                    pw.print("      GPU: ");
+                    pw.print(stringifyKBSize(gpuUsage));
+                    pw.print(" (");
+                    pw.print(stringifyKBSize(gpuDmaBufUsage));
+                    pw.print(" dmabuf + ");
+                    pw.print(stringifyKBSize(gpuPrivateUsage));
+                    pw.println(" private)");
+                    // Replace memtrack HAL reported GL category with private GPU allocations and
+                    // account it as part of kernel memory allocations
+                    ss[INDEX_TOTAL_PSS] -= ss[INDEX_TOTAL_MEMTRACK_GL];
+                    kernelUsed += gpuPrivateUsage;
+                } else {
+                    pw.print("      GPU: "); pw.println(stringifyKBSize(gpuUsage));
+                }
             }
 
-            /*
-             * Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of
-             * memInfo.getCachedSizeKb().
-             */
+             // Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of
+             // memInfo.getCachedSizeKb().
             final long lostRAM = memInfo.getTotalSizeKb()
                     - (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS])
                     - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 3ff5872..fc7a476 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1322,7 +1322,7 @@
             infoMap.put(mi.pid, mi);
         }
         updateCpuStatsNow();
-        long[] memtrackTmp = new long[1];
+        long[] memtrackTmp = new long[4];
         long[] swaptrackTmp = new long[2];
         // Get a list of Stats that have vsize > 0
         final List<ProcessCpuTracker.Stats> stats = getCpuStats(st -> st.vsize > 0);
@@ -1345,6 +1345,8 @@
         long totalPss = 0;
         long totalSwapPss = 0;
         long totalMemtrack = 0;
+        long totalMemtrackGraphics = 0;
+        long totalMemtrackGl = 0;
         for (int i = 0, size = memInfos.size(); i < size; i++) {
             ProcessMemInfo mi = memInfos.get(i);
             if (mi.pss == 0) {
@@ -1355,6 +1357,8 @@
             totalPss += mi.pss;
             totalSwapPss += mi.swapPss;
             totalMemtrack += mi.memtrack;
+            totalMemtrackGraphics += memtrackTmp[1];
+            totalMemtrackGl += memtrackTmp[2];
         }
         Collections.sort(memInfos, new Comparator<ProcessMemInfo>() {
             @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) {
@@ -1521,10 +1525,16 @@
         } else {
             final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
             if (totalExportedDmabuf >= 0) {
+                final long dmabufMapped = Debug.getDmabufMappedSizeKb();
+                final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped;
                 memInfoBuilder.append("DMA-BUF: ");
                 memInfoBuilder.append(stringifyKBSize(totalExportedDmabuf));
                 memInfoBuilder.append("\n");
-                kernelUsed += totalExportedDmabuf;
+                // Account unmapped dmabufs as part of kernel memory allocations
+                kernelUsed += dmabufUnmapped;
+                // Replace memtrack HAL reported Graphics category with mapped dmabufs
+                totalPss -= totalMemtrackGraphics;
+                totalPss += dmabufMapped;
             }
 
             final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb();
@@ -1537,19 +1547,34 @@
 
         final long gpuUsage = Debug.getGpuTotalUsageKb();
         if (gpuUsage >= 0) {
-            memInfoBuilder.append("       GPU: ");
-            memInfoBuilder.append(stringifyKBSize(gpuUsage));
-            memInfoBuilder.append("\n");
+            final long gpuDmaBufUsage = Debug.getGpuDmaBufUsageKb();
+            if (gpuDmaBufUsage >= 0) {
+                final long gpuPrivateUsage = gpuUsage - gpuDmaBufUsage;
+                memInfoBuilder.append("      GPU: ");
+                memInfoBuilder.append(stringifyKBSize(gpuUsage));
+                memInfoBuilder.append(" (");
+                memInfoBuilder.append(stringifyKBSize(gpuDmaBufUsage));
+                memInfoBuilder.append(" dmabuf + ");
+                memInfoBuilder.append(stringifyKBSize(gpuPrivateUsage));
+                memInfoBuilder.append(" private)\n");
+                // Replace memtrack HAL reported GL category with private GPU allocations and
+                // account it as part of kernel memory allocations
+                totalPss -= totalMemtrackGl;
+                kernelUsed += gpuPrivateUsage;
+            } else {
+                memInfoBuilder.append("       GPU: ");
+                memInfoBuilder.append(stringifyKBSize(gpuUsage));
+                memInfoBuilder.append("\n");
+            }
+
         }
         memInfoBuilder.append("  Used RAM: ");
         memInfoBuilder.append(stringifyKBSize(
                                   totalPss - cachedPss + kernelUsed));
         memInfoBuilder.append("\n");
 
-        /*
-         * Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of
-         * memInfo.getCachedSizeKb().
-         */
+        // Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of
+        // memInfo.getCachedSizeKb().
         memInfoBuilder.append("  Lost RAM: ");
         memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb()
                 - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
index 87b4c16..7ef315c 100644
--- a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
+++ b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
@@ -27,7 +27,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.telephony.data.EpsBearerQosSessionAttributes;
-import android.util.Slog;
+import android.util.Log;
 
 import com.android.internal.util.CollectionUtils;
 import com.android.server.ConnectivityService;
@@ -260,18 +260,18 @@
     }
 
     private static void log(final String msg) {
-        Slog.d(TAG, msg);
+        Log.d(TAG, msg);
     }
 
     private static void logw(final String msg) {
-        Slog.w(TAG, msg);
+        Log.w(TAG, msg);
     }
 
     private static void loge(final String msg) {
-        Slog.e(TAG, msg);
+        Log.e(TAG, msg);
     }
 
     private static void logwtf(final String msg) {
-        Slog.wtf(TAG, msg);
+        Log.wtf(TAG, msg);
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index cb9793f..685e9e6 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -279,6 +279,7 @@
             super.onBootPhase(phase);
             if (phase == PHASE_ACTIVITY_MANAGER_READY) {
                 mLockSettingsService.migrateOldDataAfterSystemReady();
+                mLockSettingsService.loadEscrowData();
             }
         }
 
@@ -824,13 +825,17 @@
         mSpManager.initWeaverService();
         getAuthSecretHal();
         mDeviceProvisionedObserver.onSystemReady();
-        mRebootEscrowManager.loadRebootEscrowDataIfAvailable();
+
         // TODO: maybe skip this for split system user mode.
         mStorage.prefetchUser(UserHandle.USER_SYSTEM);
         mBiometricDeferredQueue.systemReady(mInjector.getFingerprintManager(),
                 mInjector.getFaceManager());
     }
 
+    private void loadEscrowData() {
+        mRebootEscrowManager.loadRebootEscrowDataIfAvailable(mHandler);
+    }
+
     private void getAuthSecretHal() {
         try {
             mAuthSecretService = IAuthSecret.getService(/* retry */ true);
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 06962d4..53b62ca 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -21,6 +21,7 @@
 import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.os.Handler;
 import android.os.SystemClock;
 import android.os.UserManager;
 import android.provider.DeviceConfig;
@@ -39,6 +40,7 @@
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 
 import javax.crypto.SecretKey;
 
@@ -76,6 +78,13 @@
     private static final int BOOT_COUNT_TOLERANCE = 5;
 
     /**
+     * The default retry specs for loading reboot escrow data. We will attempt to retry loading
+     * escrow data on temporarily errors, e.g. unavailable network.
+     */
+    private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3;
+    private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30;
+
+    /**
      * Logs events for later debugging in bugreports.
      */
     private final RebootEscrowEventLog mEventLog;
@@ -148,6 +157,14 @@
             return null;
         }
 
+        void post(Handler handler, Runnable runnable) {
+            handler.post(runnable);
+        }
+
+        void postDelayed(Handler handler, Runnable runnable, long delayMillis) {
+            handler.postDelayed(runnable, delayMillis);
+        }
+
         public Context getContext() {
             return mContext;
         }
@@ -199,7 +216,18 @@
         mKeyStoreManager = injector.getKeyStoreManager();
     }
 
-    void loadRebootEscrowDataIfAvailable() {
+    private void onGetRebootEscrowKeyFailed(List<UserInfo> users) {
+        Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
+        for (UserInfo user : users) {
+            mStorage.removeRebootEscrow(user.id);
+        }
+
+        // Clear the old key in keystore.
+        mKeyStoreManager.clearKeyStoreEncryptionKey();
+        onEscrowRestoreComplete(false);
+    }
+
+    void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
         List<UserInfo> users = mUserManager.getUsers();
         List<UserInfo> rebootEscrowUsers = new ArrayList<>();
         for (UserInfo user : users) {
@@ -212,17 +240,49 @@
             return;
         }
 
+        mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry(
+                retryHandler, 0, users, rebootEscrowUsers));
+    }
+
+    void scheduleLoadRebootEscrowDataOrFail(Handler retryHandler, int attemptNumber,
+            List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
+        Objects.requireNonNull(retryHandler);
+
+        final int retryLimit = DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
+                "load_escrow_data_retry_count", DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT);
+        final int retryIntervalInSeconds = DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
+                "load_escrow_data_retry_interval_seconds",
+                DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
+
+        if (attemptNumber < retryLimit) {
+            Slog.i(TAG, "Scheduling loadRebootEscrowData retry number: " + attemptNumber);
+            mInjector.postDelayed(retryHandler, () -> loadRebootEscrowDataWithRetry(
+                    retryHandler, attemptNumber, users, rebootEscrowUsers),
+                    retryIntervalInSeconds * 1000);
+            return;
+        }
+
+        Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
+        onGetRebootEscrowKeyFailed(users);
+    }
+
+    void loadRebootEscrowDataWithRetry(Handler retryHandler, int attemptNumber,
+            List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
         // Fetch the key from keystore to decrypt the escrow data & escrow key; this key is
         // generated before reboot. Note that we will clear the escrow key even if the keystore key
         // is null.
         SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey();
-        RebootEscrowKey escrowKey = getAndClearRebootEscrowKey(kk);
+        RebootEscrowKey escrowKey;
+        try {
+            escrowKey = getAndClearRebootEscrowKey(kk);
+        } catch (IOException e) {
+            scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users,
+                    rebootEscrowUsers);
+            return;
+        }
+
         if (kk == null || escrowKey == null) {
-            Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
-            for (UserInfo user : users) {
-                mStorage.removeRebootEscrow(user.id);
-            }
-            onEscrowRestoreComplete(false);
+            onGetRebootEscrowKeyFailed(users);
             return;
         }
 
@@ -249,7 +309,7 @@
         }
     }
 
-    private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) {
+    private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) throws IOException {
         RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
         if (rebootEscrowProvider == null) {
             Slog.w(TAG,
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
index 6c1040b..4b00772 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
@@ -33,7 +33,7 @@
  * An implementation of the {@link RebootEscrowProviderInterface} by calling the RebootEscrow HAL.
  */
 class RebootEscrowProviderHalImpl implements RebootEscrowProviderInterface {
-    private static final String TAG = "RebootEscrowProvider";
+    private static final String TAG = "RebootEscrowProviderHal";
 
     private final Injector mInjector;
 
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
index 857ad5f..af6faad 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
@@ -16,6 +16,8 @@
 
 package com.android.server.locksettings;
 
+import java.io.IOException;
+
 import javax.crypto.SecretKey;
 
 /**
@@ -33,9 +35,10 @@
 
     /**
      * Returns the stored RebootEscrowKey, and clears the storage. If the stored key is encrypted,
-     * use the input key to decrypt the RebootEscrowKey. Returns null on failure.
+     * use the input key to decrypt the RebootEscrowKey. Returns null on failure. Throws an
+     * IOException if the failure is non-fatal, and a retry may succeed.
      */
-    RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey);
+    RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) throws IOException;
 
     /**
      * Clears the stored RebootEscrowKey.
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
index ba1a680..9d09637 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
@@ -35,7 +35,7 @@
  * encrypt & decrypt the blob.
  */
 class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterface {
-    private static final String TAG = "RebootEscrowProvider";
+    private static final String TAG = "RebootEscrowProviderServerBased";
 
     // Timeout for service binding
     private static final long DEFAULT_SERVICE_TIMEOUT_IN_SECONDS = 10;
@@ -50,6 +50,8 @@
 
     private final Injector mInjector;
 
+    private byte[] mServerBlob;
+
     static class Injector {
         private ResumeOnRebootServiceConnection mServiceConnection = null;
 
@@ -124,17 +126,20 @@
     }
 
     @Override
-    public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
-        byte[] serverBlob = mStorage.readRebootEscrowServerBlob();
+    public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) throws IOException {
+        if (mServerBlob == null) {
+            mServerBlob = mStorage.readRebootEscrowServerBlob();
+        }
         // Delete the server blob in storage.
         mStorage.removeRebootEscrowServerBlob();
-        if (serverBlob == null) {
+        if (mServerBlob == null) {
             Slog.w(TAG, "Failed to read reboot escrow server blob from storage");
             return null;
         }
 
+        Slog.i(TAG, "Loaded reboot escrow server blob from storage");
         try {
-            byte[] escrowKeyBytes = unwrapServerBlob(serverBlob, decryptionKey);
+            byte[] escrowKeyBytes = unwrapServerBlob(mServerBlob, decryptionKey);
             if (escrowKeyBytes == null) {
                 Slog.w(TAG, "Decrypted reboot escrow key bytes should not be null");
                 return null;
@@ -145,7 +150,7 @@
             }
 
             return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
-        } catch (TimeoutException | RemoteException | IOException e) {
+        } catch (TimeoutException | RemoteException e) {
             Slog.w(TAG, "Failed to decrypt the server blob ", e);
             return null;
         }
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 769b781..78c1a95 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.IPackageManager;
+import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.IInterface;
@@ -197,6 +198,11 @@
     }
 
     @Override
+    protected void ensureFilters(ServiceInfo si, int userId) {
+        // nothing to filter
+    }
+
+    @Override
     protected void loadDefaultsFromConfig() {
         String defaultDndAccess = mContext.getResources().getString(
                 R.string.config_defaultDndAccessPackages);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 21537e6..bbdcac2 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -189,6 +189,8 @@
 
     abstract protected void onServiceAdded(ManagedServiceInfo info);
 
+    abstract protected void ensureFilters(ServiceInfo si, int userId);
+
     protected List<ManagedServiceInfo> getServices() {
         synchronized (mMutex) {
             List<ManagedServiceInfo> services = new ArrayList<>(mServices);
@@ -1342,8 +1344,10 @@
             for (ComponentName component : add) {
                 try {
                     ServiceInfo info = mPm.getServiceInfo(component,
-                            PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+                            PackageManager.GET_META_DATA
+                                    | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                            userId);
                     if (info == null) {
                         Slog.w(TAG, "Not binding " + getCaption() + " service " + component
                                 + ": service not found");
@@ -1356,7 +1360,7 @@
                     }
                     Slog.v(TAG,
                             "enabling " + getCaption() + " for " + userId + ": " + component);
-                    registerService(component, userId);
+                    registerService(info, userId);
                 } catch (RemoteException e) {
                     e.rethrowFromSystemServer();
                 }
@@ -1368,9 +1372,15 @@
      * Version of registerService that takes the name of a service component to bind to.
      */
     @VisibleForTesting
-    void registerService(final ComponentName name, final int userid) {
+    void registerService(final ServiceInfo si, final int userId) {
+        ensureFilters(si, userId);
+        registerService(si.getComponentName(), userId);
+    }
+
+    @VisibleForTesting
+    void registerService(final ComponentName cn, final int userId) {
         synchronized (mMutex) {
-            registerServiceLocked(name, userid);
+            registerServiceLocked(cn, userId);
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5703ffe..917be29 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -177,6 +177,7 @@
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.UserInfo;
+import android.content.pm.VersionedPackage;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioAttributes;
@@ -214,6 +215,7 @@
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.Condition;
+import android.service.notification.ConditionProviderService;
 import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
@@ -8969,7 +8971,8 @@
         NotificationListenerFilter nls = mListeners.getNotificationListenerFilter(listener.mKey);
         if (nls != null
                 && (!nls.isTypeAllowed(notificationType)
-                || !nls.isPackageAllowed(sbn.getPackageName()))) {
+                || !nls.isPackageAllowed(
+                        new VersionedPackage(sbn.getPackageName(), sbn.getUid())))) {
             return false;
         }
         return true;
@@ -9132,6 +9135,12 @@
         }
 
         @Override
+        protected void ensureFilters(ServiceInfo si, int userId) {
+            // nothing to filter; no user visible settings for types/packages like other
+            // listeners
+        }
+
+        @Override
         @GuardedBy("mNotificationLock")
         protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
             mListeners.unregisterService(removed.service, removed.userid);
@@ -9576,11 +9585,12 @@
 
     public class NotificationListeners extends ManagedServices {
         static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners";
-        static final String TAG_REQUESTED_LISTENERS = "req_listeners";
+        static final String TAG_REQUESTED_LISTENERS = "request_listeners";
         static final String TAG_REQUESTED_LISTENER = "listener";
         static final String ATT_COMPONENT = "component";
         static final String ATT_TYPES = "types";
-        static final String ATT_PKGS = "pkgs";
+        static final String ATT_PKG = "pkg";
+        static final String ATT_UID = "uid";
         static final String TAG_APPROVED = "allowed";
         static final String TAG_DISALLOWED= "disallowed";
         static final String XML_SEPARATOR = ",";
@@ -9698,28 +9708,6 @@
         }
 
         @Override
-        public void onUserUnlocked(int user) {
-            int flags = PackageManager.GET_SERVICES | PackageManager.GET_META_DATA;
-
-            final PackageManager pmWrapper = mContext.getPackageManager();
-            List<ResolveInfo> installedServices = pmWrapper.queryIntentServicesAsUser(
-                    new Intent(getConfig().serviceInterface), flags, user);
-
-            for (ResolveInfo resolveInfo : installedServices) {
-                ServiceInfo info = resolveInfo.serviceInfo;
-
-                if (!getConfig().bindPermission.equals(info.permission)) {
-                    continue;
-                }
-                Pair key = Pair.create(info.getComponentName(), user);
-                if (!mRequestedNotificationListeners.containsKey(key)) {
-                    mRequestedNotificationListeners.put(key, new NotificationListenerFilter());
-                }
-            }
-            super.onUserUnlocked(user);
-        }
-
-        @Override
         public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uidList) {
             super.onPackagesChanged(removingPackage, pkgList, uidList);
 
@@ -9738,6 +9726,18 @@
                     }
                 }
             }
+
+            // clean up anything in the disallowed pkgs list
+            for (int i = 0; i < pkgList.length; i++) {
+                String pkg = pkgList[i];
+                int userId = UserHandle.getUserId(uidList[i]);
+                for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
+                    NotificationListenerFilter nlf = mRequestedNotificationListeners.valueAt(j);
+
+                    VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
+                    nlf.removePackage(ai);
+                }
+            }
         }
 
         @Override
@@ -9767,15 +9767,17 @@
                     int approved = FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ALERTING
                             | FLAG_FILTER_TYPE_SILENT | FLAG_FILTER_TYPE_ONGOING;
 
-                    ArraySet<String> disallowedPkgs = new ArraySet<>();
+                    ArraySet<VersionedPackage> disallowedPkgs = new ArraySet<>();
                     final int listenerOuterDepth = parser.getDepth();
                     while (XmlUtils.nextElementWithin(parser, listenerOuterDepth)) {
                         if (TAG_APPROVED.equals(parser.getName())) {
                             approved = XmlUtils.readIntAttribute(parser, ATT_TYPES);
                         } else if (TAG_DISALLOWED.equals(parser.getName())) {
-                            String pkgs = XmlUtils.readStringAttribute(parser, ATT_PKGS);
-                            if (!TextUtils.isEmpty(pkgs)) {
-                                disallowedPkgs = new ArraySet<>(pkgs.split(XML_SEPARATOR));
+                            String pkg = XmlUtils.readStringAttribute(parser, ATT_PKG);
+                            int uid = XmlUtils.readIntAttribute(parser, ATT_UID);
+                            if (!TextUtils.isEmpty(pkg)) {
+                                VersionedPackage ai = new VersionedPackage(pkg, uid);
+                                disallowedPkgs.add(ai);
                             }
                         }
                     }
@@ -9800,10 +9802,14 @@
                 XmlUtils.writeIntAttribute(out, ATT_TYPES, nlf.getTypes());
                 out.endTag(null, TAG_APPROVED);
 
-                out.startTag(null, TAG_DISALLOWED);
-                XmlUtils.writeStringAttribute(
-                        out, ATT_PKGS, String.join(XML_SEPARATOR, nlf.getDisallowedPackages()));
-                out.endTag(null, TAG_DISALLOWED);
+                for (VersionedPackage ai : nlf.getDisallowedPackages()) {
+                    if (!TextUtils.isEmpty(ai.getPackageName())) {
+                        out.startTag(null, TAG_DISALLOWED);
+                        XmlUtils.writeStringAttribute(out, ATT_PKG, ai.getPackageName());
+                        XmlUtils.writeIntAttribute(out, ATT_UID, ai.getVersionCode());
+                        out.endTag(null, TAG_DISALLOWED);
+                    }
+                }
 
                 out.endTag(null, TAG_REQUESTED_LISTENER);
             }
@@ -9821,6 +9827,38 @@
             mRequestedNotificationListeners.put(pair, nlf);
         }
 
+        @Override
+        protected void ensureFilters(ServiceInfo si, int userId) {
+            Pair listener = Pair.create(si.getComponentName(), userId);
+            NotificationListenerFilter existingNlf =
+                    mRequestedNotificationListeners.get(listener);
+            if (existingNlf  == null) {
+                // no stored filters for this listener; see if they provided a default
+                if (si.metaData != null) {
+                    String typeList = si.metaData.getString(
+                            NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES);
+                    if (typeList != null) {
+                        int types = 0;
+                        String[] typeStrings = typeList.split(XML_SEPARATOR);
+                        for (int i = 0; i < typeStrings.length; i++) {
+                            if (TextUtils.isEmpty(typeStrings[i])) {
+                                continue;
+                            }
+                            try {
+                                types |= Integer.parseInt(typeStrings[i]);
+                            } catch (NumberFormatException e) {
+                                // skip
+                            }
+                        }
+
+                         NotificationListenerFilter nlf =
+                                 new NotificationListenerFilter(types, new ArraySet<>());
+                        mRequestedNotificationListeners.put(listener, nlf);
+                    }
+                }
+            }
+        }
+
         @GuardedBy("mNotificationLock")
         public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
             if (trim == TRIM_LIGHT) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index b7b72d1..663fdee 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -335,6 +335,7 @@
 
         final List<OverlayInfo> overlaysForTarget =
                 mInterface.getOverlayInfosForTarget(overlayInfo.targetPackageName, userId);
+        overlaysForTarget.remove(overlayInfo);
         final OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
         for (final OverlayInfo disableOverlay : overlaysForTarget) {
             if ((inCategory && !Objects.equals(disableOverlay.category,overlayInfo.category))
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index de85d9e..f31d1da 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -43,6 +43,7 @@
 import android.util.ArraySet;
 import android.util.Singleton;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -226,6 +227,12 @@
     abstract ApexSessionInfo getStagedSessionInfo(int sessionId);
 
     /**
+     * Returns array of all staged sessions known to apexd.
+     */
+    @NonNull
+    abstract SparseArray<ApexSessionInfo> getSessions();
+
+    /**
      * Submit a staged session to apex service. This causes the apex service to perform some initial
      * verification and accept or reject the session. Submitting a session successfully is not
      * enough for it to be activated at the next boot, the caller needs to call
@@ -691,6 +698,21 @@
         }
 
         @Override
+        SparseArray<ApexSessionInfo> getSessions() {
+            try {
+                final ApexSessionInfo[] sessions = waitForApexService().getSessions();
+                final SparseArray<ApexSessionInfo> result = new SparseArray<>(sessions.length);
+                for (int i = 0; i < sessions.length; i++) {
+                    result.put(sessions[i].sessionId, sessions[i]);
+                }
+                return result;
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Unable to contact apexservice", re);
+                throw new RuntimeException(re);
+            }
+        }
+
+        @Override
         ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException {
             try {
                 final ApexInfoList apexInfoList = new ApexInfoList();
@@ -1083,6 +1105,11 @@
         }
 
         @Override
+        SparseArray<ApexSessionInfo> getSessions() {
+            return new SparseArray<>(0);
+        }
+
+        @Override
         ApexInfoList submitStagedSession(ApexSessionParams params)
                 throws PackageManagerException {
             throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2d393c0..2812830 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -295,23 +295,29 @@
         synchronized (mSessions) {
             for (int i = 0; i < mSessions.size(); i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
-                if (session.isStaged()) {
-                    stagedSessionsToRestore.add(session.mStagedSession);
+                if (!session.isStaged()) {
+                    continue;
+                }
+                StagingManager.StagedSession stagedSession = session.mStagedSession;
+                if (!stagedSession.isInTerminalState() && stagedSession.hasParentSessionId()
+                        && getSession(stagedSession.getParentSessionId()) == null) {
+                    stagedSession.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                            "An orphan staged session " + stagedSession.sessionId() + " is found, "
+                                + "parent " + stagedSession.getParentSessionId() + " is missing");
+                    continue;
+                }
+                if (!stagedSession.hasParentSessionId() && stagedSession.isCommitted()
+                        && !stagedSession.isInTerminalState()) {
+                    // StagingManager.restoreSessions expects a list of committed, non-finalized
+                    // parent staged sessions.
+                    stagedSessionsToRestore.add(stagedSession);
                 }
             }
         }
-        // Don't hold mSessions lock when calling restoreSession, since it might trigger an APK
+        // Don't hold mSessions lock when calling restoreSessions, since it might trigger an APK
         // atomic install which needs to query sessions, which requires lock on mSessions.
-        boolean isDeviceUpgrading = mPm.isDeviceUpgrading();
-        for (StagingManager.StagedSession session : stagedSessionsToRestore) {
-            if (!session.isInTerminalState() && session.hasParentSessionId()
-                    && getSession(session.getParentSessionId()) == null) {
-                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
-                        "An orphan staged session " + session.sessionId() + " is found, "
-                                + "parent " + session.getParentSessionId() + " is missing");
-            }
-            mStagingManager.restoreSession(session, isDeviceUpgrading);
-        }
+        // Note: restoreSessions mutates content of stagedSessionsToRestore.
+        mStagingManager.restoreSessions(stagedSessionsToRestore, mPm.isDeviceUpgrading());
     }
 
     @GuardedBy("mSessions")
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 545567c..0a74032 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -50,8 +50,6 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.storage.IStorageManager;
-import android.os.storage.StorageManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.IntArray;
@@ -63,6 +61,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
@@ -143,10 +142,16 @@
     }
 
     StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier) {
+        this(context, packageParserSupplier, ApexManager.getInstance());
+    }
+
+    @VisibleForTesting
+    StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
+            ApexManager apexManager) {
         mContext = context;
         mPackageParserSupplier = packageParserSupplier;
 
-        mApexManager = ApexManager.getInstance();
+        mApexManager = apexManager;
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mPreRebootVerificationHandler = new PreRebootVerificationHandler(
                 BackgroundThread.get().getLooper());
@@ -354,11 +359,11 @@
     }
 
     // Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
-    private void abortCheckpoint(int sessionId, String errorMsg) {
-        String failureReason = "Failed to install sessionId: " + sessionId + " Error: " + errorMsg;
+    private void abortCheckpoint(String failureReason, boolean supportsCheckpoint,
+            boolean needsCheckpoint) {
         Slog.e(TAG, failureReason);
         try {
-            if (supportsCheckpoint() && needsCheckpoint()) {
+            if (supportsCheckpoint && needsCheckpoint) {
                 // Store failure reason for next reboot
                 try (BufferedWriter writer =
                              new BufferedWriter(new FileWriter(mFailureReasonFile))) {
@@ -371,8 +376,9 @@
                 if (mApexManager.isApexSupported()) {
                     mApexManager.revertActiveSessions();
                 }
+
                 PackageHelper.getStorageManager().abortChanges(
-                        "StagingManager initiated", false /*retry*/);
+                        "abort-staged-install", false /*retry*/);
             }
         } catch (Exception e) {
             Slog.wtf(TAG, "Failed to abort checkpoint", e);
@@ -384,14 +390,6 @@
         }
     }
 
-    private boolean supportsCheckpoint() throws RemoteException {
-        return PackageHelper.getStorageManager().supportsCheckpoint();
-    }
-
-    private boolean needsCheckpoint() throws RemoteException {
-        return PackageHelper.getStorageManager().needsCheckpoint();
-    }
-
     /**
      * Utility function for extracting apex sessions out of multi-package/single session.
      */
@@ -517,96 +515,31 @@
         }
     }
 
-    private void resumeSession(@NonNull StagedSession session)
-            throws PackageManagerException {
+    private void resumeSession(@NonNull StagedSession session, boolean supportsCheckpoint,
+            boolean needsCheckpoint) throws PackageManagerException {
         Slog.d(TAG, "Resuming session " + session.sessionId());
 
         final boolean hasApex = session.containsApexSession();
-        ApexSessionInfo apexSessionInfo = null;
-        if (hasApex) {
-            // Check with apexservice whether the apex packages have been activated.
-            apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId());
-
-            // Prepare for logging a native crash during boot, if one occurred.
-            if (apexSessionInfo != null && !TextUtils.isEmpty(
-                    apexSessionInfo.crashingNativeProcess)) {
-                prepareForLoggingApexdRevert(session, apexSessionInfo.crashingNativeProcess);
-            }
-
-            if (apexSessionInfo != null && apexSessionInfo.isVerified) {
-                // Session has been previously submitted to apexd, but didn't complete all the
-                // pre-reboot verification, perhaps because the device rebooted in the meantime.
-                // Greedily re-trigger the pre-reboot verification. We want to avoid marking it as
-                // failed when not in checkpoint mode, hence it is being processed separately.
-                Slog.d(TAG, "Found pending staged session " + session.sessionId() + " still to "
-                        + "be verified, resuming pre-reboot verification");
-                mPreRebootVerificationHandler.startPreRebootVerification(session);
-                return;
-            }
-        }
 
         // Before we resume session, we check if revert is needed or not. Typically, we enter file-
         // system checkpoint mode when we reboot first time in order to install staged sessions. We
         // want to install staged sessions in this mode as rebooting now will revert user data. If
         // something goes wrong, then we reboot again to enter fs-rollback mode. Rebooting now will
         // have no effect on user data, so mark the sessions as failed instead.
-        try {
-            // If checkpoint is supported, then we only resume sessions if we are in checkpointing
-            // mode. If not, we fail all sessions.
-            if (supportsCheckpoint() && !needsCheckpoint()) {
-                String revertMsg = "Reverting back to safe state. Marking "
-                        + session.sessionId() + " as failed.";
-                final String reasonForRevert = getReasonForRevert();
-                if (!TextUtils.isEmpty(reasonForRevert)) {
-                    revertMsg += " Reason for revert: " + reasonForRevert;
-                }
-                Slog.d(TAG, revertMsg);
-                session.setSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, revertMsg);
-                return;
+        // If checkpoint is supported, then we only resume sessions if we are in checkpointing mode.
+        // If not, we fail all sessions.
+        if (supportsCheckpoint && !needsCheckpoint) {
+            String revertMsg = "Reverting back to safe state. Marking " + session.sessionId()
+                    + " as failed.";
+            final String reasonForRevert = getReasonForRevert();
+            if (!TextUtils.isEmpty(reasonForRevert)) {
+                revertMsg += " Reason for revert: " + reasonForRevert;
             }
-        } catch (RemoteException e) {
-            // Cannot continue staged install without knowing if fs-checkpoint is supported
-            Slog.e(TAG, "Checkpoint support unknown. Aborting staged install for session "
-                    + session.sessionId(), e);
-            // TODO: Mark all staged sessions together and reboot only once
-            session.setSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
-                    "Checkpoint support unknown. Aborting staged install.");
-            if (hasApex) {
-                mApexManager.revertActiveSessions();
-            }
-            mPowerManager.reboot("Checkpoint support unknown");
+            Slog.d(TAG, revertMsg);
+            session.setSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, revertMsg);
             return;
         }
 
-        // Check if apex packages in the session failed to activate
-        if (hasApex) {
-            if (apexSessionInfo == null) {
-                final String errorMsg = "apexd did not know anything about a staged session "
-                        + "supposed to be activated";
-                throw new PackageManagerException(
-                        SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
-            }
-            if (isApexSessionFailed(apexSessionInfo)) {
-                String errorMsg = "APEX activation failed. Check logcat messages from apexd "
-                        + "for more information.";
-                if (!TextUtils.isEmpty(mNativeFailureReason)) {
-                    errorMsg = "Session reverted due to crashing native process: "
-                            + mNativeFailureReason;
-                }
-                throw new PackageManagerException(
-                        SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
-            }
-            if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
-                // Apexd did not apply the session for some unknown reason. There is no
-                // guarantee that apexd will install it next time. Safer to proactively mark
-                // it as failed.
-                final String errorMsg = "Staged session " + session.sessionId() + "at boot "
-                        + "didn't activate nor fail. Marking it as failed anyway.";
-                throw new PackageManagerException(
-                        SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
-            }
-        }
-
         // Handle apk and apk-in-apex installation
         if (hasApex) {
             checkInstallationOfApkInApexSuccessful(session);
@@ -622,28 +555,24 @@
         Slog.d(TAG, "Marking session " + session.sessionId() + " as applied");
         session.setSessionApplied();
         if (hasApex) {
-            try {
-                if (supportsCheckpoint()) {
-                    // Store the session ID, which will be marked as successful by ApexManager
-                    // upon boot completion.
-                    synchronized (mSuccessfulStagedSessionIds) {
-                        mSuccessfulStagedSessionIds.add(session.sessionId());
-                    }
-                } else {
-                    // Mark sessions as successful immediately on non-checkpointing devices.
-                    mApexManager.markStagedSessionSuccessful(session.sessionId());
+            if (supportsCheckpoint) {
+                // Store the session ID, which will be marked as successful by ApexManager upon
+                // boot completion.
+                synchronized (mSuccessfulStagedSessionIds) {
+                    mSuccessfulStagedSessionIds.add(session.sessionId());
                 }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Checkpoint support unknown, marking session as successful "
-                        + "immediately.");
+            } else {
+                // Mark sessions as successful immediately on non-checkpointing devices.
                 mApexManager.markStagedSessionSuccessful(session.sessionId());
             }
         }
     }
 
-    void onInstallationFailure(StagedSession session, PackageManagerException e) {
+    void onInstallationFailure(StagedSession session, PackageManagerException e,
+            boolean supportsCheckpoint, boolean needsCheckpoint) {
         session.setSessionFailed(e.error, e.getMessage());
-        abortCheckpoint(session.sessionId(), e.getMessage());
+        abortCheckpoint("Failed to install sessionId: " + session.sessionId()
+                + " Error: " + e.getMessage(), supportsCheckpoint, needsCheckpoint);
 
         // If checkpoint is not supported, we have to handle failure for one staged session.
         if (!session.containsApexSession()) {
@@ -767,8 +696,13 @@
                     "Cannot stage session " + session.sessionId() + " with package name null");
         }
 
-        boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService(
-                Context.STORAGE_SERVICE)).isCheckpointSupported();
+        boolean supportsCheckpoint;
+        try {
+            supportsCheckpoint = PackageHelper.getStorageManager().supportsCheckpoint();
+        } catch (RemoteException e) {
+            throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                    "Can't query fs-checkpoint status : " + e);
+        }
 
         final boolean isRollback = isRollback(session);
 
@@ -911,60 +845,166 @@
                 || apexSessionInfo.isRevertFailed;
     }
 
-    void restoreSession(@NonNull StagedSession session, boolean isDeviceUpgrading) {
-        if (session.hasParentSessionId()) {
-            // Only parent sessions can be restored
-            return;
+    private void handleNonReadyAndDestroyedSessions(List<StagedSession> sessions) {
+        int j = sessions.size();
+        for (int i = 0; i < j; ) {
+            // Maintain following invariant:
+            //  * elements at positions [0, i) should be kept
+            //  * elements at positions [j, n) should be remove.
+            //  * n = sessions.size()
+            StagedSession session = sessions.get(i);
+            if (session.isDestroyed()) {
+                // Device rebooted before abandoned session was cleaned up.
+                session.abandon();
+                StagedSession session2 = sessions.set(j - 1, session);
+                sessions.set(i, session2);
+                j--;
+            } else if (!session.isSessionReady()) {
+                // The framework got restarted before the pre-reboot verification could complete,
+                // restart the verification.
+                mPreRebootVerificationHandler.startPreRebootVerification(session);
+                StagedSession session2 = sessions.set(j - 1, session);
+                sessions.set(i, session2);
+                j--;
+            } else {
+                i++;
+            }
         }
-        // Store this parent session which will be used to check overlapping later
-        createSession(session);
-        // The preconditions used during pre-reboot verification might have changed when device
-        // is upgrading. Updated staged sessions to activation failed before we resume the session.
-        StagedSession sessionToResume = session;
-        if (isDeviceUpgrading && !sessionToResume.isInTerminalState()) {
-            sessionToResume.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
-                        "Build fingerprint has changed");
-            return;
-        }
-        checkStateAndResume(sessionToResume);
+        // Delete last j elements.
+        sessions.subList(j, sessions.size()).clear();
     }
 
-    private void checkStateAndResume(@NonNull StagedSession session) {
-        // Do not resume session if boot completed already
+    void restoreSessions(@NonNull List<StagedSession> sessions, boolean isDeviceUpgrading) {
+        // Do not resume sessions if boot completed already
         if (SystemProperties.getBoolean("sys.boot_completed", false)) {
             return;
         }
 
-        if (!session.isCommitted()) {
-            // Session hasn't been committed yet, ignore.
+        for (int i = 0; i < sessions.size(); i++) {
+            StagedSession session = sessions.get(i);
+            // Quick check that PackageInstallerService gave us sessions we expected.
+            Preconditions.checkArgument(!session.hasParentSessionId(),
+                    session.sessionId() + " is a child session");
+            Preconditions.checkArgument(session.isCommitted(),
+                    session.sessionId() + " is not committed");
+            Preconditions.checkArgument(!session.isInTerminalState(),
+                    session.sessionId() + " is in terminal state");
+            // Store this parent session which will be used to check overlapping later
+            createSession(session);
+        }
+
+        if (isDeviceUpgrading) {
+            // TODO(ioffe): check that corresponding apex sessions are failed.
+            // The preconditions used during pre-reboot verification might have changed when device
+            // is upgrading. Fail all the sessions and exit early.
+            for (int i = 0; i < sessions.size(); i++) {
+                StagedSession session = sessions.get(i);
+                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                        "Build fingerprint has changed");
+            }
             return;
         }
-        // Check the state of the session and decide what to do next.
-        if (session.isSessionFailed() || session.isSessionApplied()) {
-            // Final states, nothing to do.
+
+        boolean needsCheckpoint = false;
+        boolean supportsCheckpoint = false;
+        try {
+            supportsCheckpoint = PackageHelper.getStorageManager().supportsCheckpoint();
+            needsCheckpoint = PackageHelper.getStorageManager().needsCheckpoint();
+        } catch (RemoteException e) {
+            // This means that vold has crashed, and device is in a bad state.
+            throw new IllegalStateException("Failed to get checkpoint status", e);
+        }
+
+        if (sessions.size() > 1 && !supportsCheckpoint) {
+            throw new IllegalStateException("Detected multiple staged sessions on a device without "
+                    + "fs-checkpoint support");
+        }
+
+        // Do a set of quick checks before resuming individual sessions:
+        //   1. Schedule a pre-reboot verification for non-ready sessions.
+        //   2. Abandon destroyed sessions.
+        handleNonReadyAndDestroyedSessions(sessions); // mutates |sessions|
+
+        //   3. Check state of apex sessions is consistent. All non-applied sessions will be marked
+        //      as failed.
+        final SparseArray<ApexSessionInfo> apexSessions = mApexManager.getSessions();
+        boolean hasFailedApexSession = false;
+        boolean hasAppliedApexSession = false;
+        for (int i = 0; i < sessions.size(); i++) {
+            StagedSession session = sessions.get(i);
+            if (!session.containsApexSession()) {
+                // At this point we are only interested in apex sessions.
+                continue;
+            }
+            final ApexSessionInfo apexSession = apexSessions.get(session.sessionId());
+            if (apexSession == null || apexSession.isUnknown) {
+                hasFailedApexSession = true;
+                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "apexd did "
+                        + "not know anything about a staged session supposed to be activated");
+                continue;
+            } else if (isApexSessionFailed(apexSession)) {
+                hasFailedApexSession = true;
+                String errorMsg = "APEX activation failed. Check logcat messages from apexd "
+                        + "for more information.";
+                if (!TextUtils.isEmpty(apexSession.crashingNativeProcess)) {
+                    prepareForLoggingApexdRevert(session, apexSession.crashingNativeProcess);
+                    errorMsg = "Session reverted due to crashing native process: "
+                            + apexSession.crashingNativeProcess;
+                }
+                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
+                continue;
+            } else if (apexSession.isActivated || apexSession.isSuccess) {
+                hasAppliedApexSession = true;
+                continue;
+            } else if (apexSession.isStaged) {
+                // Apexd did not apply the session for some unknown reason. There is no guarantee
+                // that apexd will install it next time. Safer to proactively mark it as failed.
+                hasFailedApexSession = true;
+                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                        "Staged session " + session.sessionId() + " at boot didn't activate nor "
+                        + "fail. Marking it as failed anyway.");
+            } else {
+                Slog.w(TAG, "Apex session " + session.sessionId() + " is in impossible state");
+                hasFailedApexSession = true;
+                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                        "Impossible state");
+            }
+        }
+
+        if (hasAppliedApexSession && hasFailedApexSession) {
+            abortCheckpoint("Found both applied and failed apex sessions", supportsCheckpoint,
+                    needsCheckpoint);
             return;
         }
-        if (session.isDestroyed()) {
-            // Device rebooted before abandoned session was cleaned up.
-            session.abandon();
+
+        if (hasFailedApexSession) {
+            // Either of those means that we failed at least one apex session, hence we should fail
+            // all other sessions.
+            for (int i = 0; i < sessions.size(); i++) {
+                StagedSession session = sessions.get(i);
+                if (session.isSessionFailed()) {
+                    // Session has been already failed in the loop above.
+                    continue;
+                }
+                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                        "Another apex session failed");
+            }
             return;
         }
-        if (!session.isSessionReady()) {
-            // The framework got restarted before the pre-reboot verification could complete,
-            // restart the verification.
-            mPreRebootVerificationHandler.startPreRebootVerification(session);
-        } else {
-            // Session had already being marked ready. Start the checks to verify if there is any
-            // follow-up work.
+
+        // Time to resume sessions.
+        for (int i = 0; i < sessions.size(); i++) {
+            StagedSession session = sessions.get(i);
             try {
-                resumeSession(session);
+                resumeSession(session, supportsCheckpoint, needsCheckpoint);
             } catch (PackageManagerException e) {
-                onInstallationFailure(session, e);
+                onInstallationFailure(session, e, supportsCheckpoint, needsCheckpoint);
             } catch (Exception e) {
                 Slog.e(TAG, "Staged install failed due to unhandled exception", e);
                 onInstallationFailure(session, new PackageManagerException(
                         SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
-                        "Staged install failed due to unhandled exception: " + e));
+                        "Staged install failed due to unhandled exception: " + e),
+                        supportsCheckpoint, needsCheckpoint);
             }
         }
     }
@@ -992,9 +1032,7 @@
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context ctx, Intent intent) {
-                mPreRebootVerificationHandler.readyToStart();
-                BackgroundThread.getExecutor().execute(
-                        () -> logFailedApexSessionsIfNecessary());
+                onBootCompletedBroadcastReceived();
                 ctx.unregisterReceiver(this);
             }
         }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
@@ -1002,6 +1040,12 @@
         mFailureReasonFile.delete();
     }
 
+    @VisibleForTesting
+    void onBootCompletedBroadcastReceived() {
+        mPreRebootVerificationHandler.readyToStart();
+        BackgroundThread.getExecutor().execute(() -> logFailedApexSessionsIfNecessary());
+    }
+
     private static class LocalIntentReceiverSync {
         private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
 
@@ -1286,9 +1330,8 @@
         private void handlePreRebootVerification_End(@NonNull StagedSession session) {
             // Before marking the session as ready, start checkpoint service if available
             try {
-                IStorageManager storageManager = PackageHelper.getStorageManager();
-                if (storageManager.supportsCheckpoint()) {
-                    storageManager.startCheckpoint(2);
+                if (PackageHelper.getStorageManager().supportsCheckpoint()) {
+                    PackageHelper.getStorageManager().startCheckpoint(2);
                 }
             } catch (Exception e) {
                 // Failed to get hold of StorageManager
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
index 0e88862..e05ef48 100644
--- a/services/core/java/com/android/server/pm/permission/OWNERS
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -1,4 +1,3 @@
-moltmann@google.com
 zhanghai@google.com
 per-file DefaultPermissionGrantPolicy.java = hackbod@android.com
 per-file DefaultPermissionGrantPolicy.java = jsharkey@android.com
@@ -7,5 +6,4 @@
 per-file DefaultPermissionGrantPolicy.java = yamasani@google.com
 per-file DefaultPermissionGrantPolicy.java = patb@google.com
 per-file DefaultPermissionGrantPolicy.java = eugenesusla@google.com
-per-file DefaultPermissionGrantPolicy.java = moltmann@google.com
 per-file DefaultPermissionGrantPolicy.java = zhanghai@google.com
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 0669581..e486f08 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2590,6 +2590,7 @@
         boolean runtimePermissionsRevoked = false;
         int[] updatedUserIds = EMPTY_INT_ARRAY;
 
+        ArraySet<String> isPrivilegedPermissionAllowlisted = null;
         ArraySet<String> shouldGrantSignaturePermission = null;
         ArraySet<String> shouldGrantInternalPermission = null;
         final List<String> requestedPermissions = pkg.getRequestedPermissions();
@@ -2604,7 +2605,14 @@
             if (permission == null) {
                 continue;
             }
-            if (permission.isSignature() && (shouldGrantSignaturePermission(pkg, permission)
+            if (permission.isPrivileged()
+                    && checkPrivilegedPermissionAllowlist(pkg, ps, permission)) {
+                if (isPrivilegedPermissionAllowlisted == null) {
+                    isPrivilegedPermissionAllowlisted = new ArraySet<>();
+                }
+                isPrivilegedPermissionAllowlisted.add(permissionName);
+            }
+            if (permission.isSignature() && (shouldGrantPermissionBySignature(pkg, permission)
                     || shouldGrantPermissionByProtectionFlags(pkg, ps, permission))) {
                 if (shouldGrantSignaturePermission == null) {
                     shouldGrantSignaturePermission = new ArraySet<>();
@@ -2830,13 +2838,17 @@
 
                     if ((bp.isNormal() && shouldGrantNormalPermission)
                             || (bp.isSignature()
-                                    && ((shouldGrantSignaturePermission != null
-                                            && shouldGrantSignaturePermission.contains(permName))
+                                    && (!bp.isPrivileged() || CollectionUtils.contains(
+                                            isPrivilegedPermissionAllowlisted, permName))
+                                    && (CollectionUtils.contains(shouldGrantSignaturePermission,
+                                            permName)
                                             || ((bp.isDevelopment() || bp.isRole())
                                                     && origState.isPermissionGranted(permName))))
                             || (bp.isInternal()
-                                    && ((shouldGrantInternalPermission != null
-                                            && shouldGrantInternalPermission.contains(permName))
+                                    && (!bp.isPrivileged() || CollectionUtils.contains(
+                                            isPrivilegedPermissionAllowlisted, permName))
+                                    && (CollectionUtils.contains(shouldGrantInternalPermission,
+                                            permName)
                                             || ((bp.isDevelopment() || bp.isRole())
                                                     && origState.isPermissionGranted(permName))))) {
                         // Grant an install permission.
@@ -3343,7 +3355,92 @@
         return allowed;
     }
 
-    private boolean shouldGrantSignaturePermission(@NonNull AndroidPackage pkg,
+    private boolean checkPrivilegedPermissionAllowlist(@NonNull AndroidPackage pkg,
+            @NonNull PackageSetting packageSetting, @NonNull Permission permission) {
+        if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {
+            return true;
+        }
+        final String packageName = pkg.getPackageName();
+        if (Objects.equals(packageName, PLATFORM_PACKAGE_NAME)) {
+            return true;
+        }
+        if (!pkg.isPrivileged()) {
+            return true;
+        }
+        if (!Objects.equals(permission.getPackageName(), PLATFORM_PACKAGE_NAME)) {
+            return true;
+        }
+        final String permissionName = permission.getName();
+        if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) {
+            return true;
+        }
+        // Only enforce the allowlist on boot
+        if (!mSystemReady
+                // Updated system apps do not need to be allowlisted
+                && !packageSetting.getPkgState().isUpdatedSystemApp()) {
+            final ApexManager apexManager = ApexManager.getInstance();
+            final String containingApexPackageName =
+                    apexManager.getActiveApexPackageNameContainingPackage(packageName);
+            final boolean isInUpdatedApex = containingApexPackageName != null
+                    && !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName,
+                    MATCH_ACTIVE_PACKAGE));
+            // Apps that are in updated apexs' do not need to be allowlisted
+            if (!isInUpdatedApex) {
+                // it's only a reportable violation if the permission isn't explicitly
+                // denied
+                if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName)) {
+                    return false;
+                }
+                Slog.w(TAG, "Privileged permission " + permissionName + " for package "
+                        + packageName + " (" + pkg.getPath()
+                        + ") not in privapp-permissions allowlist");
+                if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
+                    synchronized (mLock) {
+                        if (mPrivappPermissionsViolations == null) {
+                            mPrivappPermissionsViolations = new ArraySet<>();
+                        }
+                        mPrivappPermissionsViolations.add(packageName + " (" + pkg.getPath() + "): "
+                                + permissionName);
+                    }
+                }
+            }
+        }
+        return !RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE;
+    }
+
+    private boolean isInSystemConfigPrivAppPermissions(@NonNull AndroidPackage pkg,
+            @NonNull String permission) {
+        final SystemConfig systemConfig = SystemConfig.getInstance();
+        final Set<String> permissions;
+        if (pkg.isVendor()) {
+            permissions = systemConfig.getVendorPrivAppPermissions(pkg.getPackageName());
+        } else if (pkg.isProduct()) {
+            permissions = systemConfig.getProductPrivAppPermissions(pkg.getPackageName());
+        } else if (pkg.isSystemExt()) {
+            permissions = systemConfig.getSystemExtPrivAppPermissions(pkg.getPackageName());
+        } else {
+            permissions = systemConfig.getPrivAppPermissions(pkg.getPackageName());
+        }
+        return CollectionUtils.contains(permissions, permission);
+    }
+
+    private boolean isInSystemConfigPrivAppDenyPermissions(@NonNull AndroidPackage pkg,
+            @NonNull String permission) {
+        final SystemConfig systemConfig = SystemConfig.getInstance();
+        final Set<String> permissions;
+        if (pkg.isVendor()) {
+            permissions = systemConfig.getVendorPrivAppDenyPermissions(pkg.getPackageName());
+        } else if (pkg.isProduct()) {
+            permissions = systemConfig.getProductPrivAppDenyPermissions(pkg.getPackageName());
+        } else if (pkg.isSystemExt()) {
+            permissions = systemConfig.getSystemExtPrivAppDenyPermissions(pkg.getPackageName());
+        } else {
+            permissions = systemConfig.getPrivAppDenyPermissions(pkg.getPackageName());
+        }
+        return CollectionUtils.contains(permissions, permission);
+    }
+
+    private boolean shouldGrantPermissionBySignature(@NonNull AndroidPackage pkg,
             @NonNull Permission bp) {
         // expect single system package
         String systemPackageName = ArrayUtils.firstOrNull(mPackageManagerInt.getKnownPackageNames(
@@ -3373,8 +3470,7 @@
     private boolean shouldGrantPermissionByProtectionFlags(@NonNull AndroidPackage pkg,
             @NonNull PackageSetting pkgSetting, @NonNull Permission bp) {
         boolean allowed = false;
-        final boolean isVendorPrivilegedPermission = bp.isVendorPrivileged();
-        final boolean isPrivilegedPermission = bp.isPrivileged() || isVendorPrivilegedPermission;
+        final boolean isPrivilegedPermission = bp.isPrivileged();
         final boolean isOemPermission = bp.isOem();
         if (!allowed && (isPrivilegedPermission || isOemPermission) && pkg.isSystem()) {
             final String permissionName = bp.getName();
@@ -3386,19 +3482,18 @@
                 final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg;
                 if (disabledPkg != null && disabledPkg.getRequestedPermissions().contains(
                         permissionName)) {
-                    allowed = (isPrivilegedPermission && canGrantPrivilegedPermission(disabledPkg,
-                            true, bp)) || (isOemPermission && canGrantOemPermission(disabledPkg,
+                    allowed = (isPrivilegedPermission && disabledPkg.isPrivileged())
+                            || (isOemPermission && canGrantOemPermission(disabledPkg,
                             permissionName));
                 }
             } else {
-                allowed = (isPrivilegedPermission && canGrantPrivilegedPermission(pkg, false, bp))
+                allowed = (isPrivilegedPermission && pkg.isPrivileged())
                         || (isOemPermission && canGrantOemPermission(pkg, permissionName));
             }
             // In any case, don't grant a privileged permission to privileged vendor apps, if
             // the permission's protectionLevel does not have the extra 'vendorPrivileged'
             // flag.
-            if (allowed && isPrivilegedPermission && !isVendorPrivilegedPermission
-                    && pkg.isVendor()) {
+            if (allowed && isPrivilegedPermission && !bp.isVendorPrivileged() && pkg.isVendor()) {
                 Slog.w(TAG, "Permission " + permissionName
                         + " cannot be granted to privileged vendor apk " + pkg.getPackageName()
                         + " because it isn't a 'vendorPrivileged' permission.");
@@ -3541,90 +3636,6 @@
         return mPackageManagerInt.getPackageSetting(sourcePackageName);
     }
 
-    private boolean canGrantPrivilegedPermission(@NonNull AndroidPackage pkg,
-            boolean isUpdatedSystemApp, @NonNull Permission permission) {
-        if (!pkg.isPrivileged()) {
-            return false;
-        }
-        final boolean isPlatformPermission = PLATFORM_PACKAGE_NAME.equals(
-                permission.getPackageName());
-        if (!isPlatformPermission) {
-            return true;
-        }
-        if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {
-            return true;
-        }
-        final String permissionName = permission.getName();
-        if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) {
-            return true;
-        }
-        // Only enforce the allowlist on boot
-        if (!mSystemReady
-                // Updated system apps do not need to be allowlisted
-                && !isUpdatedSystemApp) {
-            final ApexManager apexManager = ApexManager.getInstance();
-            final String packageName = pkg.getPackageName();
-            final String containingApexPackageName =
-                    apexManager.getActiveApexPackageNameContainingPackage(packageName);
-            final boolean isInUpdatedApex = containingApexPackageName != null
-                    && !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName,
-                    MATCH_ACTIVE_PACKAGE));
-            // Apps that are in updated apexs' do not need to be allowlisted
-            if (!isInUpdatedApex) {
-                // it's only a reportable violation if the permission isn't explicitly
-                // denied
-                if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName)) {
-                    return false;
-                }
-                Slog.w(TAG, "Privileged permission " + permissionName + " for package "
-                        + packageName + " (" + pkg.getPath()
-                        + ") not in privapp-permissions allowlist");
-                if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
-                    synchronized (mLock) {
-                        if (mPrivappPermissionsViolations == null) {
-                            mPrivappPermissionsViolations = new ArraySet<>();
-                        }
-                        mPrivappPermissionsViolations.add(packageName + " (" + pkg.getPath() + "): "
-                                + permissionName);
-                    }
-                }
-            }
-        }
-        return !RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE;
-    }
-
-    private boolean isInSystemConfigPrivAppPermissions(@NonNull AndroidPackage pkg,
-            @NonNull String permission) {
-        final SystemConfig systemConfig = SystemConfig.getInstance();
-        final Set<String> permissions;
-        if (pkg.isVendor()) {
-            permissions = systemConfig.getVendorPrivAppPermissions(pkg.getPackageName());
-        } else if (pkg.isProduct()) {
-            permissions = systemConfig.getProductPrivAppPermissions(pkg.getPackageName());
-        } else if (pkg.isSystemExt()) {
-            permissions = systemConfig.getSystemExtPrivAppPermissions(pkg.getPackageName());
-        } else {
-            permissions = systemConfig.getPrivAppPermissions(pkg.getPackageName());
-        }
-        return permissions != null && permissions.contains(permission);
-    }
-
-    private boolean isInSystemConfigPrivAppDenyPermissions(@NonNull AndroidPackage pkg,
-            @NonNull String permission) {
-        final SystemConfig systemConfig = SystemConfig.getInstance();
-        final Set<String> permissions;
-        if (pkg.isVendor()) {
-            permissions = systemConfig.getVendorPrivAppDenyPermissions(pkg.getPackageName());
-        } else if (pkg.isProduct()) {
-            permissions = systemConfig.getProductPrivAppDenyPermissions(pkg.getPackageName());
-        } else if (pkg.isSystemExt()) {
-            permissions = systemConfig.getSystemExtPrivAppDenyPermissions(pkg.getPackageName());
-        } else {
-            permissions = systemConfig.getPrivAppDenyPermissions(pkg.getPackageName());
-        }
-        return permissions != null && permissions.contains(permission);
-    }
-
     private static boolean canGrantOemPermission(AndroidPackage pkg, String permission) {
         if (!pkg.isOem()) {
             return false;
diff --git a/services/core/java/com/android/server/role/OWNERS b/services/core/java/com/android/server/role/OWNERS
index b94d988..31e3549 100644
--- a/services/core/java/com/android/server/role/OWNERS
+++ b/services/core/java/com/android/server/role/OWNERS
@@ -1,5 +1,4 @@
 svetoslavganov@google.com
-moltmann@google.com
 zhanghai@google.com
 evanseverson@google.com
 eugenesusla@google.com
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bd93e04..ceebe95 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -847,8 +847,6 @@
         mWmService.openSurfaceTransaction();
         try {
             applySurfaceChangesTransaction();
-            // Send any pending task-info changes that were queued-up during a layout deferment
-            mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
             mWmService.mSyncEngine.onSurfacePlacement();
         } catch (RuntimeException e) {
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
@@ -861,6 +859,8 @@
             }
         }
 
+        // Send any pending task-info changes that were queued-up during a layout deferment
+        mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
 
         checkAppTransitionReady(surfacePlacer);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 1c4b034..a7abf6a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -149,7 +149,7 @@
         "android.hardware.power@1.1",
         "android.hardware.power-V1-cpp",
         "android.hardware.power.stats@1.0",
-        "android.hardware.power.stats-ndk_platform",
+        "android.hardware.power.stats-V1-ndk_platform",
         "android.hardware.thermal@1.0",
         "android.hardware.tv.input@1.0",
         "android.hardware.vibrator-V2-cpp",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 07eb7bf..b063e67 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -62,6 +62,7 @@
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
 import static android.app.admin.DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
@@ -92,7 +93,6 @@
 import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
 import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
 import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
-import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -157,9 +157,9 @@
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
 import android.app.admin.DevicePolicyManager.PasswordComplexity;
 import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
-import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.DevicePolicySafetyChecker;
 import android.app.admin.DeviceStateCache;
@@ -1101,7 +1101,7 @@
      */
     private void checkCanExecuteOrThrowUnsafe(@DevicePolicyOperation int operation) {
         int reason = getUnsafeOperationReason(operation);
-        if (reason == UNSAFE_OPERATION_REASON_NONE) return;
+        if (reason == OPERATION_SAFETY_REASON_NONE) return;
 
         if (mSafetyChecker == null) {
             // Happens on CTS after it's set just once (by OneTimeSafetyChecker)
@@ -1114,23 +1114,28 @@
     /**
      * Returns whether it's safe to execute the given {@code operation}, and why.
      */
-    @UnsafeOperationReason
+    @OperationSafetyReason
     int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
-        return mSafetyChecker == null ? UNSAFE_OPERATION_REASON_NONE
+        return mSafetyChecker == null ? OPERATION_SAFETY_REASON_NONE
                 : mSafetyChecker.getUnsafeOperationReason(operation);
     }
 
     @Override
     public void setNextOperationSafety(@DevicePolicyOperation int operation,
-            @UnsafeOperationReason int reason) {
+            @OperationSafetyReason int reason) {
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
         Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %s)",
                 DevicePolicyManager.operationToString(operation),
-                DevicePolicyManager.unsafeOperationReasonToString(reason)));
+                DevicePolicyManager.operationSafetyReasonToString(reason)));
         mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason);
     }
 
+    @Override
+    public boolean isSafeOperation(@OperationSafetyReason int reason) {
+        return mSafetyChecker == null ? true : mSafetyChecker.isSafeOperation(reason);
+    }
+
     // Used by DevicePolicyManagerServiceShellCommand
     List<OwnerDto> listAllOwners() {
         Preconditions.checkCallAuthorization(
@@ -7522,19 +7527,23 @@
         sendActiveAdminCommand(action, extras, deviceOwnerUserId, receiverComponent);
     }
 
-    private void sendProfileOwnerCommand(String action, Bundle extras, int userHandle) {
-        sendActiveAdminCommand(action, extras, userHandle,
-                mOwners.getProfileOwnerComponent(userHandle));
+    private void sendProfileOwnerCommand(String action, Bundle extras, @UserIdInt int userId) {
+        sendActiveAdminCommand(action, extras, userId,
+                mOwners.getProfileOwnerComponent(userId));
     }
 
     private void sendActiveAdminCommand(String action, Bundle extras,
-            int userHandle, ComponentName receiverComponent) {
+            @UserIdInt int userId, ComponentName receiverComponent) {
+        if (VERBOSE_LOG) {
+            Slog.v(LOG_TAG, "sending intent " + action + " to "
+                    + receiverComponent.flattenToShortString() + " on user " + userId);
+        }
         final Intent intent = new Intent(action);
         intent.setComponent(receiverComponent);
         if (extras != null) {
             intent.putExtras(extras);
         }
-        mContext.sendBroadcastAsUser(intent, UserHandle.of(userHandle));
+        mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
     }
 
     private void sendOwnerChangedBroadcast(String broadcast, int userId) {
@@ -12224,6 +12233,32 @@
                             packageName, findInteractAcrossProfilesResetMode(packageName), userId);
         }
 
+        @Override
+        public void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker, int reason,
+                boolean isSafe) {
+            // TODO(b/178494483): use EventLog instead
+            // TODO(b/178494483): log metrics?
+            if (VERBOSE_LOG) {
+                Slog.v(LOG_TAG, String.format("notifyUnsafeOperationStateChanged(): %s=%b",
+                        DevicePolicyManager.operationSafetyReasonToString(reason), isSafe));
+            }
+
+            Preconditions.checkArgument(mSafetyChecker == checker,
+                    "invalid checker: should be %s, was %s", mSafetyChecker, checker);
+
+            Bundle extras = new Bundle();
+            extras.putInt(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_REASON, reason);
+            extras.putBoolean(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_STATE, isSafe);
+
+            // TODO(b/178494483): add CTS test
+            sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
+                    extras);
+            for (int profileOwnerId : mOwners.getProfileOwnerKeys()) {
+                sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
+                        extras, profileOwnerId);
+            }
+        }
+
         private @Mode int findInteractAcrossProfilesResetMode(String packageName) {
             return getDefaultCrossProfilePackages().contains(packageName)
                     ? AppOpsManager.MODE_ALLOWED
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 222c987..5484a14 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -27,6 +27,7 @@
 final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
 
     private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe";
+    private static final String CMD_IS_SAFE_OPERATION_BY_REASON = "is-operation-safe-by-reason";
     private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
     private static final String CMD_LIST_OWNERS = "list-owners";
 
@@ -53,6 +54,8 @@
             switch (cmd) {
                 case CMD_IS_SAFE_OPERATION:
                     return runIsSafeOperation(pw);
+                case CMD_IS_SAFE_OPERATION_BY_REASON:
+                    return runIsSafeOperationByReason(pw);
                 case CMD_SET_SAFE_OPERATION:
                     return runSetSafeOperation(pw);
                 case CMD_LIST_OWNERS:
@@ -73,12 +76,13 @@
         return -1;
     }
 
-
     private void showHelp(PrintWriter pw) {
         pw.printf("  help\n");
         pw.printf("    Prints this help text.\n\n");
         pw.printf("  %s <OPERATION_ID>\n", CMD_IS_SAFE_OPERATION);
         pw.printf("    Checks if the give operation is safe \n\n");
+        pw.printf("  %s <REASON_ID>\n", CMD_IS_SAFE_OPERATION_BY_REASON);
+        pw.printf("    Checks if the operations are safe for the given reason\n\n");
         pw.printf("  %s <OPERATION_ID> <REASON_ID>\n", CMD_SET_SAFE_OPERATION);
         pw.printf("    Emulates the result of the next call to check if the given operation is safe"
                 + " \n\n");
@@ -89,10 +93,19 @@
     private int runIsSafeOperation(PrintWriter pw) {
         int operation = Integer.parseInt(getNextArgRequired());
         int reason = mService.getUnsafeOperationReason(operation);
-        boolean safe = reason == DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
+        boolean safe = reason == DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
         pw.printf("Operation %s is %b. Reason: %s\n",
                 DevicePolicyManager.operationToString(operation), safe,
-                DevicePolicyManager.unsafeOperationReasonToString(reason));
+                DevicePolicyManager.operationSafetyReasonToString(reason));
+        return 0;
+    }
+
+    private int runIsSafeOperationByReason(PrintWriter pw) {
+        int reason = Integer.parseInt(getNextArgRequired());
+        boolean safe = mService.isSafeOperation(reason);
+        pw.printf("Operations affected by %s are %s\n",
+                DevicePolicyManager.operationSafetyReasonToString(reason),
+                (safe ? "SAFE" : "UNSAFE"));
         return 0;
     }
 
@@ -102,7 +115,7 @@
         mService.setNextOperationSafety(operation, reason);
         pw.printf("Next call to check operation %s will return %s\n",
                 DevicePolicyManager.operationToString(operation),
-                DevicePolicyManager.unsafeOperationReasonToString(reason));
+                DevicePolicyManager.operationSafetyReasonToString(reason));
         return 0;
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
index 883f95d..7de1bd5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
@@ -15,16 +15,18 @@
  */
 package com.android.server.devicepolicy;
 
-import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
+import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
+import static android.app.admin.DevicePolicyManager.operationSafetyReasonToString;
 import static android.app.admin.DevicePolicyManager.operationToString;
-import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
 
 import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
-import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
+import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.DevicePolicySafetyChecker;
 import android.util.Slog;
 
 import com.android.internal.os.IResultReceiver;
+import com.android.server.LocalServices;
 
 import java.util.Objects;
 
@@ -43,10 +45,10 @@
     private final DevicePolicyManagerService mService;
     private final DevicePolicySafetyChecker mRealSafetyChecker;
     private final @DevicePolicyOperation int mOperation;
-    private final @UnsafeOperationReason int mReason;
+    private final @OperationSafetyReason int mReason;
 
     OneTimeSafetyChecker(DevicePolicyManagerService service,
-            @DevicePolicyOperation int operation, @UnsafeOperationReason int reason) {
+            @DevicePolicyOperation int operation, @OperationSafetyReason int reason) {
         mService = Objects.requireNonNull(service);
         mOperation = operation;
         mReason = reason;
@@ -55,24 +57,42 @@
     }
 
     @Override
-    @UnsafeOperationReason
+    @OperationSafetyReason
     public int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
         String name = operationToString(operation);
-        int reason = UNSAFE_OPERATION_REASON_NONE;
+        Slog.i(TAG, "getUnsafeOperationReason(" + name + ")");
+        int reason = OPERATION_SAFETY_REASON_NONE;
         if (operation == mOperation) {
             reason = mReason;
         } else {
             Slog.wtf(TAG, "invalid call to isDevicePolicyOperationSafe(): asked for " + name
                     + ", should be " + operationToString(mOperation));
         }
-        Slog.i(TAG, "getDevicePolicyOperationSafety(" + name + "): returning "
-                + unsafeOperationReasonToString(reason)
+        String reasonName = operationSafetyReasonToString(reason);
+        DevicePolicyManagerInternal dpmi = LocalServices
+                .getService(DevicePolicyManagerInternal.class);
+
+        Slog.i(TAG, "notifying " + reasonName + " is active");
+        dpmi.notifyUnsafeOperationStateChanged(this, reason, true);
+
+        Slog.i(TAG, "notifying " + reasonName + " is inactive");
+        dpmi.notifyUnsafeOperationStateChanged(this, reason, false);
+
+        Slog.i(TAG, "returning " + reasonName
                 + " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker);
         mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker);
         return reason;
     }
 
     @Override
+    public boolean isSafeOperation(@OperationSafetyReason int reason) {
+        boolean safe = mReason != reason;
+        Slog.i(TAG, "isSafeOperation(" + operationSafetyReasonToString(reason) + "): " + safe);
+
+        return safe;
+    }
+
+    @Override
     public void onFactoryReset(IResultReceiver callback) {
         throw new UnsupportedOperationException();
     }
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index 2219d47..cbebe69 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -38,6 +38,7 @@
 
 import android.annotation.UserIdInt;
 import android.app.Application;
+import android.app.backup.BackupManager.OperationType;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
 import android.app.backup.IFullBackupRestoreObserver;
@@ -873,7 +874,8 @@
                 SecurityException.class,
                 () ->
                         backupManagerService.requestBackup(
-                                mUserTwoId, packages, observer, monitor, 0));
+                                mUserTwoId, packages, observer, monitor, 0,
+                                OperationType.BACKUP));
     }
 
     /**
@@ -891,9 +893,11 @@
         IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
 
-        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0,
+                OperationType.BACKUP);
 
-        verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0);
+        verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0,
+                OperationType.BACKUP);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
@@ -906,9 +910,11 @@
         IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0);
+        backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0,
+                OperationType.BACKUP);
 
-        verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0);
+        verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0,
+                OperationType.BACKUP);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
@@ -921,9 +927,11 @@
         IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
-        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0,
+                OperationType.BACKUP);
 
-        verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0);
+        verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0,
+                OperationType.BACKUP);
     }
 
     /**
@@ -1084,9 +1092,11 @@
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT);
+        backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT,
+                OperationType.BACKUP);
 
-        verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+        verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT,
+                OperationType.BACKUP);
     }
 
     /** Test that the backup service does not route methods for non-registered users. */
@@ -1096,9 +1106,11 @@
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
-        backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT);
+        backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT,
+                OperationType.BACKUP);
 
-        verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+        verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT,
+                OperationType.BACKUP);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
new file mode 100644
index 0000000..195cc01
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -0,0 +1,725 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.apex.ApexSessionInfo;
+import android.content.Context;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
+import android.os.SystemProperties;
+import android.os.storage.IStorageManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.content.PackageHelper;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+@Presubmit
+@RunWith(JUnit4.class)
+public class StagingManagerTest {
+    @Rule
+    public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+    @Mock private Context mContext;
+    @Mock private IStorageManager mStorageManager;
+    @Mock private ApexManager mApexManager;
+
+    private File mTmpDir;
+    private StagingManager mStagingManager;
+
+    private MockitoSession mMockitoSession;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(eq(Context.POWER_SERVICE))).thenReturn(null);
+
+        mMockitoSession = ExtendedMockito.mockitoSession()
+                    .strictness(Strictness.LENIENT)
+                    .mockStatic(SystemProperties.class)
+                    .mockStatic(PackageHelper.class)
+                    .startMocking();
+
+        when(mStorageManager.supportsCheckpoint()).thenReturn(true);
+        when(mStorageManager.needsCheckpoint()).thenReturn(true);
+        when(PackageHelper.getStorageManager()).thenReturn(mStorageManager);
+
+        when(SystemProperties.get(eq("ro.apex.updatable"))).thenReturn("true");
+        when(SystemProperties.get(eq("ro.apex.updatable"), anyString())).thenReturn("true");
+
+        mTmpDir = mTemporaryFolder.newFolder("StagingManagerTest");
+        mStagingManager = new StagingManager(mContext, null, mApexManager);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mMockitoSession != null) {
+            mMockitoSession.finishMocking();
+        }
+    }
+
+    /**
+     * Tests that sessions committed later shouldn't cause earlier ones to fail the overlapping
+     * check.
+     */
+    @Test
+    public void checkNonOverlappingWithStagedSessions_laterSessionShouldNotFailEarlierOnes()
+            throws Exception {
+        // Create 2 sessions with overlapping packages
+        StagingManager.StagedSession session1 = createSession(111, "com.foo", 1);
+        StagingManager.StagedSession session2 = createSession(222, "com.foo", 2);
+
+        mStagingManager.createSession(session1);
+        mStagingManager.createSession(session2);
+        // Session1 should not fail in spite of the overlapping packages
+        mStagingManager.checkNonOverlappingWithStagedSessions(session1);
+        // Session2 should fail due to overlapping packages
+        assertThrows(PackageManagerException.class,
+                () -> mStagingManager.checkNonOverlappingWithStagedSessions(session2));
+    }
+
+    @Test
+    public void restoreSessions_nonParentSession_throwsIAE() throws Exception {
+        FakeStagedSession session = new FakeStagedSession(239);
+        session.setParentSessionId(1543);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mStagingManager.restoreSessions(Arrays.asList(session), false));
+    }
+
+    @Test
+    public void restoreSessions_nonCommittedSession_throwsIAE() throws Exception {
+        FakeStagedSession session = new FakeStagedSession(239);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mStagingManager.restoreSessions(Arrays.asList(session), false));
+    }
+
+    @Test
+    public void restoreSessions_terminalSession_throwsIAE() throws Exception {
+        FakeStagedSession session = new FakeStagedSession(239);
+        session.setCommitted(true);
+        session.setSessionApplied();
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mStagingManager.restoreSessions(Arrays.asList(session), false));
+    }
+
+    @Test
+    public void restoreSessions_deviceUpgrading_failsAllSessions() throws Exception {
+        FakeStagedSession session1 = new FakeStagedSession(37);
+        session1.setCommitted(true);
+        FakeStagedSession session2 = new FakeStagedSession(57);
+        session2.setCommitted(true);
+
+        mStagingManager.restoreSessions(Arrays.asList(session1, session2), true);
+
+        assertThat(session1.getErrorCode()).isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(session1.getErrorMessage()).isEqualTo("Build fingerprint has changed");
+
+        assertThat(session2.getErrorCode()).isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(session2.getErrorMessage()).isEqualTo("Build fingerprint has changed");
+    }
+
+    @Test
+    public void restoreSessions_multipleSessions_deviceWithoutFsCheckpointSupport_throwISE()
+            throws Exception {
+        FakeStagedSession session1 = new FakeStagedSession(37);
+        session1.setCommitted(true);
+        FakeStagedSession session2 = new FakeStagedSession(57);
+        session2.setCommitted(true);
+
+        when(mStorageManager.supportsCheckpoint()).thenReturn(false);
+
+        assertThrows(IllegalStateException.class,
+                () -> mStagingManager.restoreSessions(Arrays.asList(session1, session2), false));
+    }
+
+    @Test
+    public void restoreSessions_handlesDestroyedAndNotReadySessions() throws Exception {
+        FakeStagedSession destroyedApkSession = new FakeStagedSession(23);
+        destroyedApkSession.setCommitted(true);
+        destroyedApkSession.setDestroyed(true);
+
+        FakeStagedSession destroyedApexSession = new FakeStagedSession(37);
+        destroyedApexSession.setCommitted(true);
+        destroyedApexSession.setDestroyed(true);
+        destroyedApexSession.setIsApex(true);
+
+        FakeStagedSession nonReadyApkSession = new FakeStagedSession(57);
+        nonReadyApkSession.setCommitted(true);
+
+        FakeStagedSession nonReadyApexSession = new FakeStagedSession(73);
+        nonReadyApexSession.setCommitted(true);
+        nonReadyApexSession.setIsApex(true);
+
+        FakeStagedSession destroyedNonReadySession = new FakeStagedSession(101);
+        destroyedNonReadySession.setCommitted(true);
+        destroyedNonReadySession.setDestroyed(true);
+
+        FakeStagedSession regularApkSession = new FakeStagedSession(239);
+        regularApkSession.setCommitted(true);
+        regularApkSession.setSessionReady();
+
+        List<StagingManager.StagedSession> sessions = new ArrayList<>();
+        sessions.add(destroyedApkSession);
+        sessions.add(destroyedApexSession);
+        sessions.add(nonReadyApkSession);
+        sessions.add(nonReadyApexSession);
+        sessions.add(destroyedNonReadySession);
+        sessions.add(regularApkSession);
+
+        mStagingManager.restoreSessions(sessions, false);
+
+        assertThat(sessions).containsExactly(regularApkSession);
+        assertThat(destroyedApkSession.isDestroyed()).isTrue();
+        assertThat(destroyedApexSession.isDestroyed()).isTrue();
+        assertThat(destroyedNonReadySession.isDestroyed()).isTrue();
+
+        mStagingManager.onBootCompletedBroadcastReceived();
+        assertThat(nonReadyApkSession.hasPreRebootVerificationStarted()).isTrue();
+        assertThat(nonReadyApexSession.hasPreRebootVerificationStarted()).isTrue();
+    }
+
+    @Test
+    public void restoreSessions_unknownApexSession_failsAllSessions() throws Exception {
+        FakeStagedSession apkSession = new FakeStagedSession(239);
+        apkSession.setCommitted(true);
+        apkSession.setSessionReady();
+
+        FakeStagedSession apexSession = new FakeStagedSession(1543);
+        apexSession.setCommitted(true);
+        apexSession.setIsApex(true);
+        apexSession.setSessionReady();
+
+        List<StagingManager.StagedSession> sessions = new ArrayList<>();
+        sessions.add(apkSession);
+        sessions.add(apexSession);
+
+        when(mApexManager.getSessions()).thenReturn(new SparseArray<>());
+        mStagingManager.restoreSessions(sessions, false);
+
+        // Validate checkpoint wasn't aborted.
+        verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
+
+        assertThat(apexSession.getErrorCode())
+                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(apexSession.getErrorMessage()).isEqualTo("apexd did not know anything about a "
+                + "staged session supposed to be activated");
+
+        assertThat(apkSession.getErrorCode())
+                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
+    }
+
+    @Test
+    public void restoreSessions_failedApexSessions_failsAllSessions() throws Exception {
+        FakeStagedSession apkSession = new FakeStagedSession(239);
+        apkSession.setCommitted(true);
+        apkSession.setSessionReady();
+
+        FakeStagedSession apexSession1 = new FakeStagedSession(1543);
+        apexSession1.setCommitted(true);
+        apexSession1.setIsApex(true);
+        apexSession1.setSessionReady();
+
+        FakeStagedSession apexSession2 = new FakeStagedSession(101);
+        apexSession2.setCommitted(true);
+        apexSession2.setIsApex(true);
+        apexSession2.setSessionReady();
+
+        FakeStagedSession apexSession3 = new FakeStagedSession(57);
+        apexSession3.setCommitted(true);
+        apexSession3.setIsApex(true);
+        apexSession3.setSessionReady();
+
+        ApexSessionInfo activationFailed = new ApexSessionInfo();
+        activationFailed.sessionId = 1543;
+        activationFailed.isActivationFailed = true;
+
+        ApexSessionInfo staged = new ApexSessionInfo();
+        staged.sessionId = 101;
+        staged.isStaged = true;
+
+        SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>();
+        apexdSessions.put(1543, activationFailed);
+        apexdSessions.put(101, staged);
+        when(mApexManager.getSessions()).thenReturn(apexdSessions);
+
+        List<StagingManager.StagedSession> sessions = new ArrayList<>();
+        sessions.add(apkSession);
+        sessions.add(apexSession1);
+        sessions.add(apexSession2);
+        sessions.add(apexSession3);
+
+        mStagingManager.restoreSessions(sessions, false);
+
+        // Validate checkpoint wasn't aborted.
+        verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
+
+        assertThat(apexSession1.getErrorCode())
+                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(apexSession1.getErrorMessage()).isEqualTo("APEX activation failed. Check logcat "
+                + "messages from apexd for more information.");
+
+        assertThat(apexSession2.getErrorCode())
+                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(apexSession2.getErrorMessage()).isEqualTo("Staged session 101 at boot didn't "
+                + "activate nor fail. Marking it as failed anyway.");
+
+        assertThat(apexSession3.getErrorCode())
+                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(apexSession3.getErrorMessage()).isEqualTo("apexd did not know anything about a "
+                + "staged session supposed to be activated");
+
+        assertThat(apkSession.getErrorCode())
+                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
+    }
+
+    @Test
+    public void restoreSessions_stagedApexSession_failsAllSessions() throws Exception {
+        FakeStagedSession apkSession = new FakeStagedSession(239);
+        apkSession.setCommitted(true);
+        apkSession.setSessionReady();
+
+        FakeStagedSession apexSession = new FakeStagedSession(1543);
+        apexSession.setCommitted(true);
+        apexSession.setIsApex(true);
+        apexSession.setSessionReady();
+
+        ApexSessionInfo staged = new ApexSessionInfo();
+        staged.sessionId = 1543;
+        staged.isStaged = true;
+
+        SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>();
+        apexdSessions.put(1543, staged);
+        when(mApexManager.getSessions()).thenReturn(apexdSessions);
+
+        List<StagingManager.StagedSession> sessions = new ArrayList<>();
+        sessions.add(apkSession);
+        sessions.add(apexSession);
+
+        mStagingManager.restoreSessions(sessions, false);
+
+        // Validate checkpoint wasn't aborted.
+        verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
+
+        assertThat(apexSession.getErrorCode())
+                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(apexSession.getErrorMessage()).isEqualTo("Staged session 1543 at boot didn't "
+                + "activate nor fail. Marking it as failed anyway.");
+
+        assertThat(apkSession.getErrorCode())
+                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
+    }
+
+    @Test
+    public void restoreSessions_failedAndActivatedApexSessions_abortsCheckpoint() throws Exception {
+        FakeStagedSession apkSession = new FakeStagedSession(239);
+        apkSession.setCommitted(true);
+        apkSession.setSessionReady();
+
+        FakeStagedSession apexSession1 = new FakeStagedSession(1543);
+        apexSession1.setCommitted(true);
+        apexSession1.setIsApex(true);
+        apexSession1.setSessionReady();
+
+        FakeStagedSession apexSession2 = new FakeStagedSession(101);
+        apexSession2.setCommitted(true);
+        apexSession2.setIsApex(true);
+        apexSession2.setSessionReady();
+
+        FakeStagedSession apexSession3 = new FakeStagedSession(57);
+        apexSession3.setCommitted(true);
+        apexSession3.setIsApex(true);
+        apexSession3.setSessionReady();
+
+        FakeStagedSession apexSession4 = new FakeStagedSession(37);
+        apexSession4.setCommitted(true);
+        apexSession4.setIsApex(true);
+        apexSession4.setSessionReady();
+
+        ApexSessionInfo activationFailed = new ApexSessionInfo();
+        activationFailed.sessionId = 1543;
+        activationFailed.isActivationFailed = true;
+
+        ApexSessionInfo activated = new ApexSessionInfo();
+        activated.sessionId = 101;
+        activated.isActivated = true;
+
+        ApexSessionInfo staged = new ApexSessionInfo();
+        staged.sessionId = 57;
+        staged.isActivationFailed = true;
+
+        SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>();
+        apexdSessions.put(1543, activationFailed);
+        apexdSessions.put(101, activated);
+        apexdSessions.put(57, staged);
+        when(mApexManager.getSessions()).thenReturn(apexdSessions);
+
+        List<StagingManager.StagedSession> sessions = new ArrayList<>();
+        sessions.add(apkSession);
+        sessions.add(apexSession1);
+        sessions.add(apexSession2);
+        sessions.add(apexSession3);
+        sessions.add(apexSession4);
+
+        mStagingManager.restoreSessions(sessions, false);
+
+        // Validate checkpoint was aborted.
+        verify(mStorageManager, times(1)).abortChanges(eq("abort-staged-install"), eq(false));
+    }
+
+    @Test
+    public void restoreSessions_apexSessionInImpossibleState_failsAllSessions() throws Exception {
+        FakeStagedSession apkSession = new FakeStagedSession(239);
+        apkSession.setCommitted(true);
+        apkSession.setSessionReady();
+
+        FakeStagedSession apexSession = new FakeStagedSession(1543);
+        apexSession.setCommitted(true);
+        apexSession.setIsApex(true);
+        apexSession.setSessionReady();
+
+        ApexSessionInfo impossible  = new ApexSessionInfo();
+        impossible.sessionId = 1543;
+
+        SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>();
+        apexdSessions.put(1543, impossible);
+        when(mApexManager.getSessions()).thenReturn(apexdSessions);
+
+        List<StagingManager.StagedSession> sessions = new ArrayList<>();
+        sessions.add(apkSession);
+        sessions.add(apexSession);
+
+        mStagingManager.restoreSessions(sessions, false);
+
+        // Validate checkpoint wasn't aborted.
+        verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
+
+        assertThat(apexSession.getErrorCode())
+                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(apexSession.getErrorMessage()).isEqualTo("Impossible state");
+
+        assertThat(apkSession.getErrorCode())
+                .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+        assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
+    }
+
+    private StagingManager.StagedSession createSession(int sessionId, String packageName,
+            long committedMillis) {
+        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        params.isStaged = true;
+
+        InstallSource installSource = InstallSource.create("testInstallInitiator",
+                "testInstallOriginator", "testInstaller", "testAttributionTag");
+
+        PackageInstallerSession session = new PackageInstallerSession(
+                /* callback */ null,
+                /* context */ null,
+                /* pm */ null,
+                /* sessionProvider */ null,
+                /* looper */ BackgroundThread.getHandler().getLooper(),
+                /* stagingManager */ null,
+                /* sessionId */ sessionId,
+                /* userId */ 456,
+                /* installerUid */ -1,
+                /* installSource */ installSource,
+                /* sessionParams */ params,
+                /* createdMillis */ 0L,
+                /* committedMillis */ committedMillis,
+                /* stageDir */ mTmpDir,
+                /* stageCid */ null,
+                /* files */ null,
+                /* checksums */ null,
+                /* prepared */ true,
+                /* committed */ true,
+                /* destroyed */ false,
+                /* sealed */ false,  // Setting to true would trigger some PM logic.
+                /* childSessionIds */ null,
+                /* parentSessionId */ -1,
+                /* isReady */ false,
+                /* isFailed */ false,
+                /* isApplied */false,
+                /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.STAGED_SESSION_NO_ERROR,
+                /* stagedSessionErrorMessage */ "no error");
+
+        StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
+        doReturn(packageName).when(stagedSession).getPackageName();
+        doAnswer(invocation -> {
+            Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
+            return filter.test(stagedSession);
+        }).when(stagedSession).sessionContains(any());
+        return stagedSession;
+    }
+
+    private static final class FakeStagedSession implements StagingManager.StagedSession {
+        private final int mSessionId;
+        private boolean mIsApex = false;
+        private boolean mIsCommitted = false;
+        private boolean mIsReady = false;
+        private boolean mIsApplied = false;
+        private boolean mIsFailed = false;
+        private @StagedSessionErrorCode int mErrorCode = -1;
+        private String mErrorMessage;
+        private boolean mIsDestroyed = false;
+        private int mParentSessionId = -1;
+        private String mPackageName;
+        private boolean mIsAbandonded = false;
+        private boolean mPreRebootVerificationStarted = false;
+        private final List<StagingManager.StagedSession> mChildSessions = new ArrayList<>();
+
+        private FakeStagedSession(int sessionId) {
+            mSessionId = sessionId;
+        }
+
+        private void setParentSessionId(int parentSessionId) {
+            mParentSessionId = parentSessionId;
+        }
+
+        private void setCommitted(boolean isCommitted) {
+            mIsCommitted = isCommitted;
+        }
+
+        private void setIsApex(boolean isApex) {
+            mIsApex = isApex;
+        }
+
+        private void setDestroyed(boolean isDestroyed) {
+            mIsDestroyed = isDestroyed;
+        }
+
+        private void setPackageName(String packageName) {
+            mPackageName = packageName;
+        }
+
+        private boolean isAbandonded() {
+            return mIsAbandonded;
+        }
+
+        private boolean hasPreRebootVerificationStarted() {
+            return mPreRebootVerificationStarted;
+        }
+
+        private FakeStagedSession addChildSession(FakeStagedSession session) {
+            mChildSessions.add(session);
+            session.setParentSessionId(sessionId());
+            return this;
+        }
+
+        private @StagedSessionErrorCode int getErrorCode() {
+            return mErrorCode;
+        }
+
+        private String getErrorMessage() {
+            return mErrorMessage;
+        }
+
+        @Override
+        public boolean isMultiPackage() {
+            return !mChildSessions.isEmpty();
+        }
+
+        @Override
+        public boolean isApexSession() {
+            return mIsApex;
+        }
+
+        @Override
+        public boolean isCommitted() {
+            return mIsCommitted;
+        }
+
+        @Override
+        public boolean isInTerminalState() {
+            return isSessionApplied() || isSessionFailed();
+        }
+
+        @Override
+        public boolean isDestroyed() {
+            return mIsDestroyed;
+        }
+
+        @Override
+        public boolean isSessionReady() {
+            return mIsReady;
+        }
+
+        @Override
+        public boolean isSessionApplied() {
+            return mIsApplied;
+        }
+
+        @Override
+        public boolean isSessionFailed() {
+            return mIsFailed;
+        }
+
+        @Override
+        public List<StagingManager.StagedSession> getChildSessions() {
+            return mChildSessions;
+        }
+
+        @Override
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        @Override
+        public int getParentSessionId() {
+            return mParentSessionId;
+        }
+
+        @Override
+        public int sessionId() {
+            return mSessionId;
+        }
+
+        @Override
+        public PackageInstaller.SessionParams sessionParams() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean sessionContains(Predicate<StagingManager.StagedSession> filter) {
+            return filter.test(this);
+        }
+
+        @Override
+        public boolean containsApkSession() {
+            Preconditions.checkState(!hasParentSessionId(), "Child session");
+            if (!isMultiPackage()) {
+                return !isApexSession();
+            }
+            for (StagingManager.StagedSession session : mChildSessions) {
+                if (!session.isApexSession()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public boolean containsApexSession() {
+            Preconditions.checkState(!hasParentSessionId(), "Child session");
+            if (!isMultiPackage()) {
+                return isApexSession();
+            }
+            for (StagingManager.StagedSession session : mChildSessions) {
+                if (session.isApexSession()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public void setSessionReady() {
+            mIsReady = true;
+        }
+
+        @Override
+        public void setSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) {
+            Preconditions.checkState(!mIsApplied, "Already marked as applied");
+            mIsFailed = true;
+            mErrorCode = errorCode;
+            mErrorMessage = errorMessage;
+        }
+
+        @Override
+        public void setSessionApplied() {
+            Preconditions.checkState(!mIsFailed, "Already marked as failed");
+            mIsApplied = true;
+        }
+
+        @Override
+        public void installSession(IntentSender statusReceiver) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean hasParentSessionId() {
+            return mParentSessionId != -1;
+        }
+
+        @Override
+        public long getCommittedMillis() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void abandon() {
+            mIsAbandonded = true;
+        }
+
+        @Override
+        public boolean notifyStartPreRebootVerification() {
+            mPreRebootVerificationStarted = true;
+            // TODO(ioffe): change to true when tests for pre-reboot verification are added.
+            return false;
+        }
+
+        @Override
+        public void notifyEndPreRebootVerification() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void verifySession() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index af11fe1..b98f025 100644
--- a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -23,7 +23,6 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.when;
 
-import android.app.backup.BackupAgent;
 import android.app.backup.BackupManager.OperationType;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
@@ -31,7 +30,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import com.android.internal.backup.IBackupTransport;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -58,7 +56,6 @@
     @Mock IBackupObserver mBackupObserver;
     @Mock PackageManager mPackageManager;
     @Mock TransportClient mTransportClient;
-    @Mock IBackupTransport mBackupTransport;
     @Mock BackupEligibilityRules mBackupEligibilityRules;
 
 
@@ -135,29 +132,6 @@
         assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules);
     }
 
-    @Test
-    public void testGetOperationTypeFromTransport_returnsMigrationForMigrationTransport()
-            throws Exception {
-        when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport);
-        when(mBackupTransport.getTransportFlags()).thenReturn(
-                BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER);
-
-        int operationType = mService.getOperationTypeFromTransport(mTransportClient);
-
-        assertThat(operationType).isEqualTo(OperationType.MIGRATION);
-    }
-
-    @Test
-    public void testGetOperationTypeFromTransport_returnsBackupByDefault()
-            throws Exception {
-        when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport);
-        when(mBackupTransport.getTransportFlags()).thenReturn(0);
-
-        int operationType = mService.getOperationTypeFromTransport(mTransportClient);
-
-        assertThat(operationType).isEqualTo(OperationType.BACKUP);
-    }
-
     private static PackageInfo getPackageInfo(String packageName) {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.applicationInfo = new ApplicationInfo();
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index 353ac4b..f9b25d9 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -39,6 +39,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
 
@@ -51,7 +52,7 @@
     private static final String TAG = "WorkerCountTrackerTest";
 
     private static final double[] EQUAL_PROBABILITY_CDF =
-            buildCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES,
+            buildWorkTypeCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES,
                     1.0 / NUM_WORK_TYPES);
 
     private Random mRandom;
@@ -64,18 +65,19 @@
     }
 
     @NonNull
-    private static double[] buildCdf(double pTop, double pEj, double pBg, double pBgUser) {
-        double[] cdf = new double[JobConcurrencyManager.NUM_WORK_TYPES];
+    private static double[] buildWorkTypeCdf(double pTop, double pEj, double pBg, double pBgUser) {
+        return buildCdf(pTop, pEj, pBg, pBgUser);
+    }
+
+    @NonNull
+    private static double[] buildCdf(double... probs) {
+        double[] cdf = new double[probs.length];
         double sum = 0;
 
-        sum += pTop;
-        cdf[0] = sum;
-        sum += pEj;
-        cdf[1] = sum;
-        sum += pBg;
-        cdf[2] = sum;
-        sum += pBgUser;
-        cdf[3] = sum;
+        for (int i = 0; i < probs.length; ++i) {
+            sum += probs[i];
+            cdf[i] = sum;
+        }
 
         if (Double.compare(1, sum) != 0) {
             throw new IllegalArgumentException("probabilities don't sum to one: " + sum);
@@ -83,25 +85,30 @@
         return cdf;
     }
 
-    @JobConcurrencyManager.WorkType
-    static int getRandomWorkType(double[] cdf, double rand) {
+    static int getRandomIndex(double[] cdf, double rand) {
         for (int i = cdf.length - 1; i >= 0; --i) {
             if (rand < cdf[i] && (i == 0 || rand > cdf[i - 1])) {
-                switch (i) {
-                    case 0:
-                        return WORK_TYPE_TOP;
-                    case 1:
-                        return WORK_TYPE_EJ;
-                    case 2:
-                        return WORK_TYPE_BG;
-                    case 3:
-                        return WORK_TYPE_BGUSER;
-                    default:
-                        throw new IllegalStateException("Unknown work type");
-                }
+                return i;
             }
         }
-        throw new IllegalStateException("Couldn't pick random work type");
+        throw new IllegalStateException("Couldn't pick random index");
+    }
+
+    @JobConcurrencyManager.WorkType
+    static int getRandomWorkType(double[] cdf, double rand) {
+        final int index = getRandomIndex(cdf, rand);
+        switch (index) {
+            case 0:
+                return WORK_TYPE_TOP;
+            case 1:
+                return WORK_TYPE_EJ;
+            case 2:
+                return WORK_TYPE_BG;
+            case 3:
+                return WORK_TYPE_BGUSER;
+            default:
+                throw new IllegalStateException("Unknown work type");
+        }
     }
 
     /**
@@ -110,25 +117,59 @@
     class Jobs {
         public final SparseIntArray running = new SparseIntArray();
         public final SparseIntArray pending = new SparseIntArray();
+        public final List<Integer> pendingMultiTypes = new ArrayList<>();
 
-        public void maybeEnqueueJobs(double probStart, double[] typeCdf) {
+        /**
+         * @param probStart   Probability of starting a job
+         * @param typeCdf     The CDF representing the probability of each work type
+         * @param numTypesCdf The CDF representing the probability of a job having X different
+         *                    work types. Each index i represents i+1 work types (ie. index 0 = 1
+         *                    work type, index 3 = 4 work types).
+         */
+        public void maybeEnqueueJobs(double probStart, double[] typeCdf, double[] numTypesCdf) {
+            assertThat(numTypesCdf.length).isAtMost(NUM_WORK_TYPES);
+            assertThat(numTypesCdf.length).isAtLeast(1);
+
             while (mRandom.nextDouble() < probStart) {
-                final int workType = getRandomWorkType(typeCdf, mRandom.nextDouble());
-                pending.put(workType, pending.get(workType) + 1);
+                final int numTypes = getRandomIndex(numTypesCdf, mRandom.nextDouble()) + 1;
+                int types = WORK_TYPE_NONE;
+                for (int i = 0; i < numTypes; ++i) {
+                    types |= getRandomWorkType(typeCdf, mRandom.nextDouble());
+                }
+                addPending(types, 1);
             }
         }
 
-        public void maybeFinishJobs(double probStop) {
-            for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
-                if (mRandom.nextDouble() < probStop) {
-                    running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
-                    mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
+        void addPending(int allWorkTypes, int num) {
+            for (int n = 0; n < num; ++n) {
+                for (int i = 0; i < 32; ++i) {
+                    final int type = 1 << i;
+                    if ((allWorkTypes & type) != 0) {
+                        pending.put(type, pending.get(type) + 1);
+                    }
+                }
+                pendingMultiTypes.add(allWorkTypes);
+            }
+        }
+
+        void removePending(int allWorkTypes) {
+            for (int i = 0; i < 32; ++i) {
+                final int type = 1 << i;
+                if ((allWorkTypes & type) != 0) {
+                    pending.put(type, pending.get(type) - 1);
                 }
             }
-            for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) {
-                if (mRandom.nextDouble() < probStop) {
-                    running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1);
-                    mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
+            pendingMultiTypes.remove(Integer.valueOf(allWorkTypes));
+        }
+
+        public void maybeFinishJobs(double probStop) {
+            for (int i = running.size() - 1; i >= 0; --i) {
+                final int workType = running.keyAt(i);
+                for (int c = running.valueAt(i); c > 0; --c) {
+                    if (mRandom.nextDouble() < probStop) {
+                        running.put(workType, running.get(workType) - 1);
+                        mWorkCountTracker.onJobFinished(workType);
+                    }
                 }
             }
         }
@@ -171,6 +212,15 @@
         return false;
     }
 
+    private int getPendingMultiType(Jobs jobs, @JobConcurrencyManager.WorkType int workType) {
+        for (int multiType : jobs.pendingMultiTypes) {
+            if ((multiType & workType) != 0) {
+                return multiType;
+            }
+        }
+        throw new IllegalStateException("No pending multi type with work type: " + workType);
+    }
+
     private void startPendingJobs(Jobs jobs) {
         while (hasStartablePendingJob(jobs)) {
             final int startingWorkType =
@@ -178,9 +228,10 @@
 
             if (jobs.pending.get(startingWorkType) > 0
                     && mWorkCountTracker.canJobStart(startingWorkType) != WORK_TYPE_NONE) {
-                jobs.pending.put(startingWorkType, jobs.pending.get(startingWorkType) - 1);
+                final int pendingMultiType = getPendingMultiType(jobs, startingWorkType);
+                jobs.removePending(pendingMultiType);
                 jobs.running.put(startingWorkType, jobs.running.get(startingWorkType) + 1);
-                mWorkCountTracker.stageJob(startingWorkType);
+                mWorkCountTracker.stageJob(startingWorkType, pendingMultiType);
                 mWorkCountTracker.onJobStarted(startingWorkType);
             }
         }
@@ -192,12 +243,17 @@
     private void checkRandom(Jobs jobs, int numTests, int totalMax,
             @NonNull List<Pair<Integer, Integer>> minLimits,
             @NonNull List<Pair<Integer, Integer>> maxLimits,
-            double probStart, double[] typeCdf, double probStop) {
+            double probStart, double[] typeCdf, double[] numTypesCdf, double probStop) {
+        int minExpected = 0;
+        for (Pair<Integer, Integer> minLimit : minLimits) {
+            minExpected = Math.min(minLimit.second, minExpected);
+        }
         for (int i = 0; i < numTests; i++) {
             jobs.maybeFinishJobs(probStop);
-            jobs.maybeEnqueueJobs(probStart, typeCdf);
+            jobs.maybeEnqueueJobs(probStart, typeCdf, numTypesCdf);
 
             recount(jobs, totalMax, minLimits, maxLimits);
+            final int numPending = jobs.pendingMultiTypes.size();
             startPendingJobs(jobs);
 
             int totalRunning = 0;
@@ -209,6 +265,7 @@
                 totalRunning += numRunning;
             }
             assertThat(totalRunning).isAtMost(totalMax);
+            assertThat(totalRunning).isAtLeast(Math.min(minExpected, numPending));
             for (Pair<Integer, Integer> maxLimit : maxLimits) {
                 assertWithMessage("Work type " + maxLimit.first + " is running too many jobs")
                         .that(jobs.running.get(maxLimit.first)).isAtMost(maxLimit.second);
@@ -233,7 +290,7 @@
         final double probStart = 0.1;
 
         checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
-                EQUAL_PROBABILITY_CDF, probStop);
+                EQUAL_PROBABILITY_CDF, EQUAL_PROBABILITY_CDF, probStop);
     }
 
     @Test
@@ -246,10 +303,12 @@
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of();
         final double probStop = 0.5;
-        final double[] cdf = buildCdf(0.5, 0, 0.5, 0);
+        final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0);
+        final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -262,10 +321,12 @@
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.5;
-        final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+        final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+        final double[] numTypesCdf = buildCdf(.75, .2, .05);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -278,10 +339,12 @@
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of();
         final double probStop = 0.5;
-        final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+        final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+        final double[] numTypesCdf = buildCdf(.05, .95);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -294,10 +357,12 @@
                 List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.5;
-        final double[] cdf = buildCdf(0.1, 0, 0.8, .1);
+        final double[] cdf = buildWorkTypeCdf(0.1, 0, 0.8, .1);
+        final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -310,10 +375,12 @@
                 List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.5;
-        final double[] cdf = buildCdf(0.9, 0, 0.1, 0);
+        final double[] cdf = buildWorkTypeCdf(0.9, 0, 0.1, 0);
+        final double[] numTypesCdf = buildCdf(1);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -326,10 +393,12 @@
                 List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.4;
-        final double[] cdf = buildCdf(0.1, 0, 0.1, .8);
+        final double[] cdf = buildWorkTypeCdf(0.1, 0, 0.1, .8);
+        final double[] numTypesCdf = buildCdf(0.5, 0.5);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -343,10 +412,12 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final double probStop = 0.4;
-        final double[] cdf = buildCdf(0.9, 0, 0.05, 0.05);
+        final double[] cdf = buildWorkTypeCdf(0.9, 0, 0.05, 0.05);
+        final double[] numTypesCdf = buildCdf(1);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -360,10 +431,12 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final double probStop = 0.5;
-        final double[] cdf = buildCdf(0, 0, 0.5, 0.5);
+        final double[] cdf = buildWorkTypeCdf(0, 0, 0.5, 0.5);
+        final double[] numTypesCdf = buildCdf(1);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -377,10 +450,12 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final double probStop = 0.5;
-        final double[] cdf = buildCdf(0, 0, 0.1, 0.9);
+        final double[] cdf = buildWorkTypeCdf(0, 0, 0.1, 0.9);
+        final double[] numTypesCdf = buildCdf(0.9, 0.1);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -394,10 +469,12 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final double probStop = 0.5;
-        final double[] cdf = buildCdf(0, 0, 0.9, 0.1);
+        final double[] cdf = buildWorkTypeCdf(0, 0, 0.9, 0.1);
+        final double[] numTypesCdf = buildCdf(1);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -410,10 +487,12 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.4;
-        final double[] cdf = buildCdf(0.5, 0.5, 0, 0);
+        final double[] cdf = buildWorkTypeCdf(0.5, 0.5, 0, 0);
+        final double[] numTypesCdf = buildCdf(0.1, 0.7, 0.2);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -430,10 +509,11 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1));
         final double probStop = 0.01;
+        final double[] numTypesCdf = buildCdf(0, 0.05, 0.05, 0.9);
         final double probStart = 0.99;
 
         checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
-                EQUAL_PROBABILITY_CDF, probStop);
+                EQUAL_PROBABILITY_CDF, numTypesCdf, probStop);
     }
 
     @Test
@@ -446,10 +526,12 @@
                 List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.4;
-        final double[] cdf = buildCdf(.1, 0.5, 0.35, 0.05);
+        final double[] cdf = buildWorkTypeCdf(.1, 0.5, 0.35, 0.05);
+        final double[] numTypesCdf = buildCdf(1);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     @Test
@@ -464,10 +546,12 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.4;
-        final double[] cdf = buildCdf(0.01, 0.49, 0.1, 0.4);
+        final double[] cdf = buildWorkTypeCdf(0.01, 0.49, 0.1, 0.4);
+        final double[] numTypesCdf = buildCdf(0.7, 0.3);
         final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
     }
 
     /** Used by the following tests */
@@ -483,7 +567,7 @@
             jobs.running.put(run.first, run.second);
         }
         for (Pair<Integer, Integer> pend : pending) {
-            jobs.pending.put(pend.first, pend.second);
+            jobs.addPending(pend.first, pend.second);
         }
 
         recount(jobs, totalMax, minLimits, maxLimits);
@@ -651,14 +735,32 @@
                         Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
                 /* resPen */ List.of(
                         Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 2)));
+
+        // Test multi-types
+        checkSimple(6,
+                /* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
+                /* run */ List.of(),
+                /* pen */ List.of(
+                        // 2 of these as TOP, 1 as EJ
+                        Pair.create(WORK_TYPE_TOP | WORK_TYPE_EJ, 3),
+                        // 1 as EJ, 2 as BG
+                        Pair.create(WORK_TYPE_EJ | WORK_TYPE_BG, 3),
+                        Pair.create(WORK_TYPE_BG, 4),
+                        Pair.create(WORK_TYPE_BGUSER, 1)),
+                /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2),
+                        Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
+                /* resPen */ List.of(
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)));
     }
 
     /** Tests that the counter updates properly when jobs are stopped. */
     @Test
     public void testJobLifecycleLoop() {
         final Jobs jobs = new Jobs();
-        jobs.pending.put(WORK_TYPE_TOP, 11);
-        jobs.pending.put(WORK_TYPE_BG, 10);
+        jobs.addPending(WORK_TYPE_TOP, 11);
+        jobs.addPending(WORK_TYPE_BG, 10);
 
         final int totalMax = 6;
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 1));
@@ -729,4 +831,149 @@
         assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
         assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(3);
     }
+
+    /** Tests that the counter updates properly when jobs are stopped. */
+    @Test
+    public void testJobLifecycleLoop_Multitype() {
+        final Jobs jobs = new Jobs();
+        jobs.addPending(WORK_TYPE_TOP, 6); // a
+        jobs.addPending(WORK_TYPE_TOP | WORK_TYPE_EJ, 5); // b
+        jobs.addPending(WORK_TYPE_BG, 10); // c
+
+        final int totalMax = 8;
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 1), Pair.create(WORK_TYPE_BG, 1));
+        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 5));
+
+        recount(jobs, totalMax, minLimits, maxLimits);
+
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(11);
+        assertThat(jobs.pending.get(WORK_TYPE_EJ)).isEqualTo(5);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(10);
+
+        startPendingJobs(jobs);
+
+        assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(6);
+        assertThat(jobs.running.get(WORK_TYPE_EJ)).isEqualTo(1);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(4);
+        // Since starting happens in random order, all EJs could have run first.
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(9);
+
+        // Stop all jobs
+        jobs.maybeFinishJobs(1);
+
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_TOP);
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_EJ)).isEqualTo(WORK_TYPE_EJ);
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+        startPendingJobs(jobs);
+
+        assertThat(jobs.running.get(WORK_TYPE_TOP) + jobs.running.get(WORK_TYPE_EJ)).isEqualTo(4);
+        // Depending on the order jobs start, we may run all TOP/EJ combos as TOP and reserve a slot
+        // for EJ, which would reduce BG count to 3 instead of 4.
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isAtLeast(3);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isAtMost(4);
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+        assertThat(jobs.pending.get(WORK_TYPE_EJ)).isEqualTo(0);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtLeast(5);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtMost(6);
+
+        // Stop only a bg job and make sure the counter only allows another bg job to start.
+        jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) - 1);
+        mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
+
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_NONE);
+        // Depending on the order jobs start, we may run all TOP/EJ combos as TOP and reserve a slot
+        // for EJ, which would reduce BG count to 3 instead of 4.
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+        startPendingJobs(jobs);
+
+        assertThat(jobs.running.get(WORK_TYPE_TOP) + jobs.running.get(WORK_TYPE_EJ)).isEqualTo(4);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isAtLeast(3);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isAtMost(4);
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+        assertThat(jobs.pending.get(WORK_TYPE_EJ)).isEqualTo(0);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtLeast(4);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtMost(5);
+    }
+
+    /** Tests that the counter updates properly when jobs are stopped. */
+    @Test
+    public void testJobLifecycleLoop_Multitype_RandomOrder() {
+        final Jobs jobs = new Jobs();
+        SparseIntArray multiToCount = new SparseIntArray();
+        multiToCount.put(WORK_TYPE_TOP, 6); // a
+        multiToCount.put(WORK_TYPE_TOP | WORK_TYPE_EJ, 5); // b
+        multiToCount.put(WORK_TYPE_EJ | WORK_TYPE_BG, 5); // c
+        multiToCount.put(WORK_TYPE_BG, 5); // d
+        while (multiToCount.size() > 0) {
+            final int index = mRandom.nextInt(multiToCount.size());
+            final int count = multiToCount.valueAt(index);
+            jobs.addPending(multiToCount.keyAt(index), 1);
+            if (count <= 1) {
+                multiToCount.removeAt(index);
+            } else {
+                multiToCount.put(multiToCount.keyAt(index), count - 1);
+            }
+        }
+
+        final int totalMax = 8;
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 1), Pair.create(WORK_TYPE_BG, 1));
+        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 5));
+
+        recount(jobs, totalMax, minLimits, maxLimits);
+
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(11);
+        assertThat(jobs.pending.get(WORK_TYPE_EJ)).isEqualTo(10);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(10);
+
+        startPendingJobs(jobs);
+
+        // Random order, but we should have 6 TOP, 1 EJ, and 1 BG running.
+        assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(6);
+        assertThat(jobs.running.get(WORK_TYPE_EJ)).isEqualTo(1);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(4);
+        // Can't equate pending EJ since some could be running as TOP and BG
+        assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(9);
+
+        // Stop all jobs
+        jobs.maybeFinishJobs(1);
+
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_TOP);
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_EJ)).isEqualTo(WORK_TYPE_EJ);
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+        startPendingJobs(jobs);
+
+        // Random order, but we should have 4 TOP, 1 EJ, and 1 BG running.
+        assertThat(jobs.running.get(WORK_TYPE_TOP)).isAtLeast(1);
+        assertThat(jobs.running.get(WORK_TYPE_EJ)).isAtLeast(1);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+        // At this point, all TOP should be running (or have already run).
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+        assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(5);
+
+        // Stop only a bg job and make sure the counter only allows another bg job to start.
+        jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) - 1);
+        mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
+
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_NONE);
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_EJ)).isEqualTo(WORK_TYPE_NONE);
+        assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+        startPendingJobs(jobs);
+
+        assertThat(jobs.running.get(WORK_TYPE_TOP)).isAtLeast(1);
+        assertThat(jobs.running.get(WORK_TYPE_EJ)).isAtLeast(1);
+        assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+        assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+        assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2);
+        assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(4);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index a4ba4c8..a896f1b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -43,6 +43,7 @@
 import android.content.ContextWrapper;
 import android.content.pm.UserInfo;
 import android.hardware.rebootescrow.IRebootEscrow;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.os.UserManager;
@@ -155,6 +156,11 @@
         }
 
         @Override
+        void post(Handler handler, Runnable runnable) {
+            runnable.run();
+        }
+
+        @Override
         public UserManager getUserManager() {
             return mUserManager;
         }
@@ -369,7 +375,7 @@
 
     @Test
     public void loadRebootEscrowDataIfAvailable_NothingAvailable_Success() throws Exception {
-        mService.loadRebootEscrowDataIfAvailable();
+        mService.loadRebootEscrowDataIfAvailable(null);
     }
 
     @Test
@@ -401,7 +407,7 @@
         doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
         when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
 
-        mService.loadRebootEscrowDataIfAvailable();
+        mService.loadRebootEscrowDataIfAvailable(null);
         verify(mRebootEscrow).retrieveKey();
         assertTrue(metricsSuccessCaptor.getValue());
         verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
@@ -435,7 +441,7 @@
 
         when(mServiceConnection.unwrap(any(), anyLong()))
                 .thenAnswer(invocation -> invocation.getArgument(0));
-        mService.loadRebootEscrowDataIfAvailable();
+        mService.loadRebootEscrowDataIfAvailable(null);
         verify(mServiceConnection).unwrap(any(), anyLong());
         assertTrue(metricsSuccessCaptor.getValue());
         verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
@@ -466,7 +472,7 @@
         when(mInjected.getBootCount()).thenReturn(10);
         when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
 
-        mService.loadRebootEscrowDataIfAvailable();
+        mService.loadRebootEscrowDataIfAvailable(null);
         verify(mRebootEscrow).retrieveKey();
         verify(mInjected, never()).reportMetric(anyBoolean());
     }
@@ -493,7 +499,7 @@
         when(mInjected.getBootCount()).thenReturn(10);
         when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
 
-        mService.loadRebootEscrowDataIfAvailable();
+        mService.loadRebootEscrowDataIfAvailable(null);
         verify(mInjected, never()).reportMetric(anyBoolean());
     }
 
@@ -527,7 +533,7 @@
         when(mInjected.getBootCount()).thenReturn(10);
         when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
 
-        mService.loadRebootEscrowDataIfAvailable();
+        mService.loadRebootEscrowDataIfAvailable(null);
         verify(mInjected).reportMetric(eq(true));
     }
 
@@ -557,7 +563,7 @@
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
         doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
         when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> new byte[32]);
-        mService.loadRebootEscrowDataIfAvailable();
+        mService.loadRebootEscrowDataIfAvailable(null);
         verify(mRebootEscrow).retrieveKey();
         assertFalse(metricsSuccessCaptor.getValue());
     }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
index bc1e025..28b737b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
@@ -30,6 +30,7 @@
 
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
@@ -42,7 +43,6 @@
 import org.mockito.stubbing.Answer;
 
 import java.io.File;
-import java.io.IOException;
 
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
@@ -130,7 +130,7 @@
     @Test
     public void getAndClearRebootEscrowKey_ServiceConnectionException_failure() throws Exception {
         when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
-        doThrow(IOException.class).when(mServiceConnection).unwrap(any(), anyLong());
+        doThrow(RemoteException.class).when(mServiceConnection).unwrap(any(), anyLong());
 
         assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
         mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
diff --git a/services/tests/servicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/StagingManagerTest.java
deleted file mode 100644
index 79935c2..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/StagingManagerTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import android.content.Context;
-import android.content.pm.PackageInstaller;
-import android.os.storage.StorageManager;
-import android.platform.test.annotations.Presubmit;
-
-import com.android.internal.os.BackgroundThread;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.util.function.Predicate;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-
-@Presubmit
-@RunWith(JUnit4.class)
-public class StagingManagerTest {
-    @Rule
-    public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-
-    private File mTmpDir;
-    private StagingManager mStagingManager;
-
-    @Before
-    public void setup() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        StorageManager storageManager = Mockito.mock(StorageManager.class);
-        Context context = Mockito.mock(Context.class);
-        when(storageManager.isCheckpointSupported()).thenReturn(true);
-        when(context.getSystemService(eq(Context.POWER_SERVICE))).thenReturn(null);
-        when(context.getSystemService(eq(Context.STORAGE_SERVICE))).thenReturn(storageManager);
-
-        mTmpDir = mTemporaryFolder.newFolder("StagingManagerTest");
-        mStagingManager = new StagingManager(context, null);
-    }
-
-    /**
-     * Tests that sessions committed later shouldn't cause earlier ones to fail the overlapping
-     * check.
-     */
-    @Test
-    public void checkNonOverlappingWithStagedSessions_laterSessionShouldNotFailEarlierOnes()
-            throws Exception {
-        // Create 2 sessions with overlapping packages
-        StagingManager.StagedSession session1 = createSession(111, "com.foo", 1);
-        StagingManager.StagedSession session2 = createSession(222, "com.foo", 2);
-
-        mStagingManager.createSession(session1);
-        mStagingManager.createSession(session2);
-        // Session1 should not fail in spite of the overlapping packages
-        mStagingManager.checkNonOverlappingWithStagedSessions(session1);
-        // Session2 should fail due to overlapping packages
-        assertThrows(PackageManagerException.class,
-                () -> mStagingManager.checkNonOverlappingWithStagedSessions(session2));
-    }
-
-    private StagingManager.StagedSession createSession(int sessionId, String packageName,
-            long committedMillis) {
-        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
-                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-        params.isStaged = true;
-
-        InstallSource installSource = InstallSource.create("testInstallInitiator",
-                "testInstallOriginator", "testInstaller", "testAttributionTag");
-
-        PackageInstallerSession session = new PackageInstallerSession(
-                /* callback */ null,
-                /* context */ null,
-                /* pm */ null,
-                /* sessionProvider */ null,
-                /* looper */ BackgroundThread.getHandler().getLooper(),
-                /* stagingManager */ null,
-                /* sessionId */ sessionId,
-                /* userId */ 456,
-                /* installerUid */ -1,
-                /* installSource */ installSource,
-                /* sessionParams */ params,
-                /* createdMillis */ 0L,
-                /* committedMillis */ committedMillis,
-                /* stageDir */ mTmpDir,
-                /* stageCid */ null,
-                /* files */ null,
-                /* checksums */ null,
-                /* prepared */ true,
-                /* committed */ true,
-                /* destroyed */ false,
-                /* sealed */ false,  // Setting to true would trigger some PM logic.
-                /* childSessionIds */ null,
-                /* parentSessionId */ -1,
-                /* isReady */ false,
-                /* isFailed */ false,
-                /* isApplied */false,
-                /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.STAGED_SESSION_NO_ERROR,
-                /* stagedSessionErrorMessage */ "no error");
-
-        StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
-        doReturn(packageName).when(stagedSession).getPackageName();
-        doAnswer(invocation -> {
-            Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
-            return filter.test(stagedSession);
-        }).when(stagedSession).sessionContains(any());
-        return stagedSession;
-    }
-}
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/OWNERS b/services/tests/servicestests/test-apps/ConnTestApp/OWNERS
new file mode 100644
index 0000000..aa87958
--- /dev/null
+++ b/services/tests/servicestests/test-apps/ConnTestApp/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/net/OWNERS
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index ec28baf..07475e9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -1686,6 +1686,11 @@
         }
 
         @Override
+        protected void ensureFilters(ServiceInfo si, int userId) {
+
+        }
+
+        @Override
         protected void loadDefaultsFromConfig() {
             mDefaultComponents.addAll(mDefaults);
         }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index afcf08ef..80a046a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -15,6 +15,11 @@
  */
 package com.android.server.notification;
 
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
+
+import static com.android.server.notification.NotificationManagerService.NotificationListeners.TAG_REQUESTED_LISTENERS;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -27,11 +32,14 @@
 import android.content.ComponentName;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.VersionedPackage;
+import android.os.Bundle;
 import android.service.notification.NotificationListenerFilter;
+import android.service.notification.NotificationListenerService;
 import android.util.ArraySet;
 import android.util.Pair;
+import android.util.Slog;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
@@ -47,8 +55,6 @@
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.List;
 
 public class NotificationListenersTest extends UiServiceTestCase {
 
@@ -80,22 +86,21 @@
 
     @Test
     public void testReadExtraTag() throws Exception {
-        String xml = "<req_listeners>"
+        String xml = "<" + TAG_REQUESTED_LISTENERS+ ">"
                 + "<listener component=\"" + mCn1.flattenToString() + "\" user=\"0\">"
                 + "<allowed types=\"7\" />"
-                + "<disallowed pkgs=\"\" />"
                 + "</listener>"
                 + "<listener component=\"" + mCn2.flattenToString() + "\" user=\"10\">"
                 + "<allowed types=\"4\" />"
-                + "<disallowed pkgs=\"something\" />"
+                + "<disallowed pkg=\"pkg1\" uid=\"243\"/>"
                 + "</listener>"
-                + "</req_listeners>";
+                + "</" + TAG_REQUESTED_LISTENERS + ">";
 
         TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
-        mListeners.readExtraTag("req_listeners", parser);
+        mListeners.readExtraTag(TAG_REQUESTED_LISTENERS, parser);
 
         validateListenersFromXml();
     }
@@ -103,8 +108,9 @@
     @Test
     public void testWriteExtraTag() throws Exception {
         NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+        VersionedPackage a1 = new VersionedPackage("pkg1", 243);
         NotificationListenerFilter nlf2 =
-                new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+                new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
         mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
         mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
 
@@ -134,16 +140,18 @@
 
         assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)).getTypes())
                 .isEqualTo(4);
+        VersionedPackage a1 = new VersionedPackage("pkg1", 243);
         assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10))
                 .getDisallowedPackages())
-                .contains("something");
+                .contains(a1);
     }
 
     @Test
     public void testOnUserRemoved() {
         NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+        VersionedPackage a1 = new VersionedPackage("pkg1", 243);
         NotificationListenerFilter nlf2 =
-                new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+                new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
         mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
         mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
 
@@ -155,58 +163,68 @@
     }
 
     @Test
-    public void testOnUserUnlocked() {
-        // one exists already, say from xml
-        NotificationListenerFilter nlf =
-                new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
-        mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf);
-
-        // new service exists or backfilling on upgrade to S
+    public void testEnsureFilters_newServiceNoMetadata() {
         ServiceInfo si = new ServiceInfo();
-        si.permission = mListeners.getConfig().bindPermission;
+        si.packageName = "new2";
+        si.name = "comp2";
+
+        mListeners.ensureFilters(si, 0);
+
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))).isNull();
+    }
+
+    @Test
+    public void testEnsureFilters_preExisting() {
+        // one exists already, say from xml
+        VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+        NotificationListenerFilter nlf =
+                new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
+        mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf);
+        ServiceInfo siOld = new ServiceInfo();
+        siOld.packageName = mCn2.getPackageName();
+        siOld.name = mCn2.getClassName();
+
+        mListeners.ensureFilters(siOld, 0);
+
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))).isEqualTo(nlf);
+    }
+
+    @Test
+    public void testEnsureFilters_newServiceWithMetadata() {
+        ServiceInfo si = new ServiceInfo();
         si.packageName = "new";
         si.name = "comp";
-        ResolveInfo ri = new ResolveInfo();
-        ri.serviceInfo = si;
+        si.metaData = new Bundle();
+        si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1,2");
 
-        // incorrect service
-        ServiceInfo si2 = new ServiceInfo();
-        ResolveInfo ri2 = new ResolveInfo();
-        ri2.serviceInfo = si2;
-        si2.packageName = "new2";
-        si2.name = "comp2";
-
-        List<ResolveInfo> ris = new ArrayList<>();
-        ris.add(ri);
-        ris.add(ri2);
-
-        when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(ris);
-
-        mListeners.onUserUnlocked(0);
-
-        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0)).getTypes())
-                .isEqualTo(4);
-        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))
-                .getDisallowedPackages())
-                .contains("something");
+        mListeners.ensureFilters(si, 0);
 
         assertThat(mListeners.getNotificationListenerFilter(
                 Pair.create(si.getComponentName(), 0)).getTypes())
-                .isEqualTo(15);
-        assertThat(mListeners.getNotificationListenerFilter(Pair.create(si.getComponentName(), 0))
-                .getDisallowedPackages())
-                .isEmpty();
+                .isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ALERTING);
+    }
 
-        assertThat(mListeners.getNotificationListenerFilter(Pair.create(si2.getComponentName(), 0)))
-                .isNull();
+    @Test
+    public void testEnsureFilters_newServiceWithEmptyMetadata() {
+        ServiceInfo si = new ServiceInfo();
+        si.packageName = "new";
+        si.name = "comp";
+        si.metaData = new Bundle();
+        si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "");
 
+        mListeners.ensureFilters(si, 0);
+
+        assertThat(mListeners.getNotificationListenerFilter(
+                Pair.create(si.getComponentName(), 0)).getTypes())
+                .isEqualTo(0);
     }
 
     @Test
     public void testOnPackageChanged() {
         NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+        VersionedPackage a1 = new VersionedPackage("pkg1", 243);
         NotificationListenerFilter nlf2 =
-                new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+                new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
         mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
         mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
 
@@ -224,8 +242,9 @@
     @Test
     public void testOnPackageChanged_removing() {
         NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+        VersionedPackage a1 = new VersionedPackage("pkg1", 243);
         NotificationListenerFilter nlf2 =
-                new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+                new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
         mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
         mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2);
 
@@ -239,4 +258,21 @@
                 .isEqualTo(4);
     }
 
+    @Test
+    public void testOnPackageChanged_removingDisallowedPackage() {
+        NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+        VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+        NotificationListenerFilter nlf2 =
+                new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
+        mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
+        mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2);
+
+        String[] pkgs = new String[] {"pkg1"};
+        int[] uids = new int[] {243};
+        mListeners.onPackagesChanged(true, pkgs, uids);
+
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))
+                .getDisallowedPackages()).isEmpty();
+    }
+
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e8888f4..a640509 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -464,7 +464,7 @@
 
         // Setup managed services
         when(mNlf.isTypeAllowed(anyInt())).thenReturn(true);
-        when(mNlf.isPackageAllowed(anyString())).thenReturn(true);
+        when(mNlf.isPackageAllowed(any())).thenReturn(true);
         when(mNlf.isPackageAllowed(null)).thenReturn(true);
         when(mListeners.getNotificationListenerFilter(any())).thenReturn(mNlf);
         mListener = mListeners.new ManagedServiceInfo(
@@ -7307,7 +7307,7 @@
 
     @Test
     public void testIsVisibleToListener_disallowedPackage() {
-        when(mNlf.isPackageAllowed(null)).thenReturn(false);
+        when(mNlf.isPackageAllowed(any())).thenReturn(false);
 
         StatusBarNotification sbn = mock(StatusBarNotification.class);
         when(sbn.getUserId()).thenReturn(10);
diff --git a/services/usb/OWNERS b/services/usb/OWNERS
index 8ee72b5..60172a3 100644
--- a/services/usb/OWNERS
+++ b/services/usb/OWNERS
@@ -1,6 +1,5 @@
 badhri@google.com
 elaurent@google.com
-moltmann@google.com
 albertccwang@google.com
 jameswei@google.com
 howardyen@google.com
\ No newline at end of file
diff --git a/telephony/java/android/telephony/RadioInterfaceCapabilities.java b/telephony/java/android/telephony/RadioInterfaceCapabilities.java
deleted file mode 100644
index 7c7eb9f..0000000
--- a/telephony/java/android/telephony/RadioInterfaceCapabilities.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony;
-
-import android.util.ArraySet;
-
-/**
- * Contains the set of supported capabilities that the Radio Interface supports on this device.
- *
- * @hide
- */
-public class RadioInterfaceCapabilities {
-
-    private final ArraySet<String> mSupportedCapabilities;
-
-
-    public RadioInterfaceCapabilities() {
-        mSupportedCapabilities = new ArraySet<>();
-    }
-
-    /**
-     * Marks a capability as supported
-     *
-     * @param capabilityName the name of the capability
-     */
-    public void addSupportedCapability(
-            @TelephonyManager.RadioInterfaceCapability String capabilityName) {
-        mSupportedCapabilities.add(capabilityName);
-    }
-
-    /**
-     * Whether the capability is supported
-     *
-     * @param capabilityName the name of the capability
-     */
-    public boolean isSupported(String capabilityName) {
-        return mSupportedCapabilities.contains(capabilityName);
-    }
-}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 16ffd9e..ee3a0ef 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8491,6 +8491,11 @@
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * <p>
+     * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
+     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
+     * setPreferredNetworkTypesBitmap is used instead.
      *
      * @param subId the id of the subscription to set the preferred network type for.
      * @param networkType the preferred network type
@@ -8524,6 +8529,11 @@
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * <p>
+     * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
+     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
+     * setPreferredNetworkTypesBitmap is used instead.
      *
      * @param networkTypeBitmask The bitmask of preferred network types.
      * @return true on success; false on any failure.
@@ -8550,6 +8560,11 @@
      * Set the allowed network types of the device. This is for carrier or privileged apps to
      * enable/disable certain network types on the device. The user preferred network types should
      * be set through {@link #setPreferredNetworkTypeBitmask}.
+     * <p>
+     * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
+     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
+     * setPreferredNetworkTypesBitmap is used instead.
      *
      * @param allowedNetworkTypes The bitmask of allowed network types.
      * @return true on success; false on any failure.
@@ -8624,6 +8639,11 @@
      * </ol>
      * This API will result in allowing an intersection of allowed network types for all reasons,
      * including the configuration done through other reasons.
+     * <p>
+     * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
+     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
+     * setPreferredNetworkTypesBitmap is used instead.
      *
      * @param reason the reason the allowed network type change is taking place
      * @param allowedNetworkTypes The bitmask of allowed network types.
@@ -14861,10 +14881,24 @@
     public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE =
             "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
 
+    /**
+     * Indicates whether {@link #setPreferredNetworkType}, {@link
+     * #setPreferredNetworkTypeBitmask}, {@link #setAllowedNetworkTypes} and
+     * {@link #setAllowedNetworkTypesForReason} rely on
+     * setAllowedNetworkTypesBitmap instead of setPreferredNetworkTypesBitmap on the radio
+     * interface.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED =
+            "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @StringDef(prefix = "CAPABILITY_", value = {
             CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
+            CAPABILITY_ALLOWED_NETWORK_TYPES_USED,
     })
     public @interface RadioInterfaceCapability {}