Merge "Adding tests for temporary and renewable trust" into tm-dev
diff --git a/Android.bp b/Android.bp
index 753cefc..edaa11e3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -97,6 +97,7 @@
         ":platform-compat-native-aidl",
 
         // AIDL sources from external directories
+        ":android.hardware.gnss-V2-java-source",
         ":android.hardware.graphics.common-V3-java-source",
         ":android.hardware.security.keymint-V2-java-source",
         ":android.hardware.security.secureclock-V1-java-source",
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 18f63b7..23056b5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -17,6 +17,7 @@
 package com.android.server.job;
 
 import static com.android.server.job.JobSchedulerService.MAX_JOB_CONTEXTS_COUNT;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
 import android.annotation.IntDef;
@@ -32,9 +33,11 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.UserInfo;
+import android.os.BatteryStats;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.util.ArraySet;
@@ -51,19 +54,24 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IBatteryStats;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.util.StatLogger;
 import com.android.server.JobSchedulerBackgroundThread;
 import com.android.server.LocalServices;
 import com.android.server.job.controllers.JobStatus;
 import com.android.server.job.controllers.StateController;
+import com.android.server.job.restrictions.JobRestriction;
 import com.android.server.pm.UserManagerInternal;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 /**
  * This class decides, given the various configuration and the system status, which jobs can start
@@ -278,6 +286,11 @@
 
     String[] mRecycledShouldStopJobReason = new String[MAX_JOB_CONTEXTS_COUNT];
 
+    /**
+     * Set of JobServiceContexts that we use to run jobs.
+     */
+    final List<JobServiceContext> mActiveServices = new ArrayList<>();
+
     private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>();
 
     private final WorkCountTracker mWorkCountTracker = new WorkCountTracker();
@@ -358,6 +371,20 @@
         onInteractiveStateChanged(mPowerManager.isInteractive());
     }
 
+    /**
+     * Called when the boot phase reaches
+     * {@link com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START}.
+     */
+    void onThirdPartyAppsCanStart() {
+        final IBatteryStats batteryStats = IBatteryStats.Stub.asInterface(
+                ServiceManager.getService(BatteryStats.SERVICE_NAME));
+        for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
+            mActiveServices.add(
+                    new JobServiceContext(mService, this, batteryStats,
+                            mService.mJobPackageTracker, mContext.getMainLooper()));
+        }
+    }
+
     @GuardedBy("mLock")
     void onAppRemovedLocked(String pkgName, int uid) {
         final PackageStats packageStats = mActivePkgStats.get(UserHandle.getUserId(uid), pkgName);
@@ -390,6 +417,7 @@
                 case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
                     if (mPowerManager != null && mPowerManager.isDeviceIdleMode()) {
                         synchronized (mLock) {
+                            stopUnexemptedJobsForDoze();
                             stopLongRunningJobsLocked("deep doze");
                         }
                     }
@@ -471,6 +499,11 @@
     }
 
     @GuardedBy("mLock")
+    ArraySet<JobStatus> getRunningJobsLocked() {
+        return mRunningJobs;
+    }
+
+    @GuardedBy("mLock")
     boolean isJobRunningLocked(JobStatus job) {
         return mRunningJobs.contains(job);
     }
@@ -546,7 +579,7 @@
         }
 
         final List<JobStatus> pendingJobs = mService.mPendingJobs;
-        final List<JobServiceContext> activeServices = mService.mActiveServices;
+        final List<JobServiceContext> activeServices = mActiveServices;
 
         // To avoid GC churn, we recycle the arrays.
         JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap;
@@ -719,9 +752,44 @@
     }
 
     @GuardedBy("mLock")
+    boolean stopJobOnServiceContextLocked(JobStatus job,
+            @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
+        if (!mRunningJobs.contains(job)) {
+            return false;
+        }
+
+        for (int i = 0; i < mActiveServices.size(); i++) {
+            JobServiceContext jsc = mActiveServices.get(i);
+            final JobStatus executing = jsc.getRunningJobLocked();
+            if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
+                jsc.cancelExecutingJobLocked(reason, internalReasonCode, debugReason);
+                return true;
+            }
+        }
+        Slog.wtf(TAG, "Couldn't find running job on a context");
+        mRunningJobs.remove(job);
+        return false;
+    }
+
+    @GuardedBy("mLock")
+    private void stopUnexemptedJobsForDoze() {
+        // When becoming idle, make sure no jobs are actively running,
+        // except those using the idle exemption flag.
+        for (int i = 0; i < mActiveServices.size(); i++) {
+            JobServiceContext jsc = mActiveServices.get(i);
+            final JobStatus executing = jsc.getRunningJobLocked();
+            if (executing != null && !executing.canRunInDoze()) {
+                jsc.cancelExecutingJobLocked(JobParameters.STOP_REASON_DEVICE_STATE,
+                        JobParameters.INTERNAL_STOP_REASON_DEVICE_IDLE,
+                        "cancelled due to doze");
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
     private void stopLongRunningJobsLocked(@NonNull String debugReason) {
         for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; ++i) {
-            final JobServiceContext jsc = mService.mActiveServices.get(i);
+            final JobServiceContext jsc = mActiveServices.get(i);
             final JobStatus jobStatus = jsc.getRunningJobLocked();
 
             if (jobStatus != null && !jsc.isWithinExecutionGuaranteeTime()) {
@@ -731,6 +799,41 @@
         }
     }
 
+    @GuardedBy("mLock")
+    void stopNonReadyActiveJobsLocked() {
+        for (int i = 0; i < mActiveServices.size(); i++) {
+            JobServiceContext serviceContext = mActiveServices.get(i);
+            final JobStatus running = serviceContext.getRunningJobLocked();
+            if (running == null) {
+                continue;
+            }
+            if (!running.isReady()) {
+                if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX
+                        && running.getStopReason() == JobParameters.STOP_REASON_APP_STANDBY) {
+                    serviceContext.cancelExecutingJobLocked(
+                            running.getStopReason(),
+                            JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET,
+                            "cancelled due to restricted bucket");
+                } else {
+                    serviceContext.cancelExecutingJobLocked(
+                            running.getStopReason(),
+                            JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
+                            "cancelled due to unsatisfied constraints");
+                }
+            } else {
+                final JobRestriction restriction = mService.checkIfRestricted(running);
+                if (restriction != null) {
+                    final int internalReasonCode = restriction.getInternalReason();
+                    serviceContext.cancelExecutingJobLocked(restriction.getReason(),
+                            internalReasonCode,
+                            "restricted due to "
+                                    + JobParameters.getInternalReasonCodeDescription(
+                                    internalReasonCode));
+                }
+            }
+        }
+    }
+
     private void noteConcurrency() {
         mService.mJobPackageTracker.noteConcurrency(mRunningJobs.size(),
                 // TODO: log per type instead of only TOP
@@ -1078,6 +1181,24 @@
     }
 
     @GuardedBy("mLock")
+    boolean executeTimeoutCommandLocked(PrintWriter pw, String pkgName, int userId,
+            boolean hasJobId, int jobId) {
+        boolean foundSome = false;
+        for (int i = 0; i < mActiveServices.size(); i++) {
+            final JobServiceContext jc = mActiveServices.get(i);
+            final JobStatus js = jc.getRunningJobLocked();
+            if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
+                foundSome = true;
+                pw.print("Timing out: ");
+                js.printUniqueId(pw);
+                pw.print(" ");
+                pw.println(js.getServiceComponent().flattenToShortString());
+            }
+        }
+        return foundSome;
+    }
+
+    @GuardedBy("mLock")
     private String printPendingQueueLocked() {
         StringBuilder s = new StringBuilder("Pending queue: ");
         Iterator<JobStatus> it = mService.mPendingJobs.iterator();
@@ -1200,6 +1321,43 @@
         }
     }
 
+    @GuardedBy("mLock")
+    void dumpActiveJobsLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate,
+            long nowElapsed, long nowUptime) {
+        pw.println("Active jobs:");
+        pw.increaseIndent();
+        for (int i = 0; i < mActiveServices.size(); i++) {
+            JobServiceContext jsc = mActiveServices.get(i);
+            final JobStatus job = jsc.getRunningJobLocked();
+
+            if (job != null && !predicate.test(job)) {
+                continue;
+            }
+
+            pw.print("Slot #"); pw.print(i); pw.print(": ");
+            jsc.dumpLocked(pw, nowElapsed);
+
+            if (job != null) {
+                pw.increaseIndent();
+
+                pw.increaseIndent();
+                job.dump(pw, false, nowElapsed);
+                pw.decreaseIndent();
+
+                pw.print("Evaluated bias: ");
+                pw.println(JobInfo.getBiasString(job.lastEvaluatedBias));
+
+                pw.print("Active at ");
+                TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
+                pw.print(", pending for ");
+                TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
+                pw.decreaseIndent();
+                pw.println();
+            }
+        }
+        pw.decreaseIndent();
+    }
+
     public void dumpProtoLocked(ProtoOutputStream proto, long tag, long now, long nowRealtime) {
         final long token = proto.start(tag);
 
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 b936278..3d74bc9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -57,7 +57,6 @@
 import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.BatteryManagerInternal;
-import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Handler;
@@ -67,7 +66,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
@@ -90,7 +88,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
@@ -100,7 +97,6 @@
 import com.android.server.DeviceIdleInternal;
 import com.android.server.JobSchedulerBackgroundThread;
 import com.android.server.LocalServices;
-import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
 import com.android.server.job.controllers.BackgroundJobsController;
 import com.android.server.job.controllers.BatteryController;
@@ -243,12 +239,6 @@
     static final int MSG_CHECK_CHANGED_JOB_LIST = 8;
     static final int MSG_CHECK_MEDIA_EXEMPTION = 9;
 
-    /**
-     * Track Services that have currently active or pending jobs. The index is provided by
-     * {@link JobStatus#getServiceToken()}
-     */
-    final List<JobServiceContext> mActiveServices = new ArrayList<>();
-
     /** List of controllers that will notify this service of updates to jobs. */
     final List<StateController> mControllers;
     /**
@@ -307,7 +297,6 @@
 
     PackageManagerInternal mLocalPM;
     ActivityManagerInternal mActivityManagerInternal;
-    IBatteryStats mBatteryStats;
     DeviceIdleInternal mLocalDeviceIdleController;
     @VisibleForTesting
     AppStateTrackerImpl mAppStateTracker;
@@ -1578,7 +1567,8 @@
             mJobPackageTracker.noteNonpending(cancelled);
         }
         // Cancel if running.
-        stopJobOnServiceContextLocked(cancelled, reason, internalReasonCode, debugReason);
+        mConcurrencyManager.stopJobOnServiceContextLocked(
+                cancelled, reason, internalReasonCode, debugReason);
         // If this is a replacement, bring in the new version of the job
         if (incomingJob != null) {
             if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
@@ -1627,19 +1617,7 @@
             if (DEBUG) {
                 Slog.d(TAG, "Doze state changed: " + deviceIdle);
             }
-            if (deviceIdle) {
-                // When becoming idle, make sure no jobs are actively running,
-                // except those using the idle exemption flag.
-                for (int i=0; i<mActiveServices.size(); i++) {
-                    JobServiceContext jsc = mActiveServices.get(i);
-                    final JobStatus executing = jsc.getRunningJobLocked();
-                    if (executing != null && !executing.canRunInDoze()) {
-                        jsc.cancelExecutingJobLocked(JobParameters.STOP_REASON_DEVICE_STATE,
-                                JobParameters.INTERNAL_STOP_REASON_DEVICE_IDLE,
-                                "cancelled due to doze");
-                    }
-                }
-            } else {
+            if (!deviceIdle) {
                 // When coming out of idle, allow thing to start back up.
                 if (mReadyToRock) {
                     if (mLocalDeviceIdleController != null) {
@@ -1682,10 +1660,10 @@
         // active is true if pending queue contains jobs OR some job is running.
         boolean active = mPendingJobs.size() > 0;
         if (mPendingJobs.size() <= 0) {
-            for (int i=0; i<mActiveServices.size(); i++) {
-                final JobServiceContext jsc = mActiveServices.get(i);
-                final JobStatus job = jsc.getRunningJobLocked();
-                if (job != null && !job.canRunInDoze()) {
+            final ArraySet<JobStatus> runningJobs = mConcurrencyManager.getRunningJobsLocked();
+            for (int i = runningJobs.size() - 1; i >= 0; --i) {
+                final JobStatus job = runningJobs.valueAt(i);
+                if (!job.canRunInDoze()) {
                     // We will report active if we have a job running and it does not have an
                     // exception that allows it to run in Doze.
                     active = true;
@@ -1895,16 +1873,9 @@
             synchronized (mLock) {
                 // Let's go!
                 mReadyToRock = true;
-                mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
-                        BatteryStats.SERVICE_NAME));
                 mLocalDeviceIdleController =
                         LocalServices.getService(DeviceIdleInternal.class);
-                // Create the "runners".
-                for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
-                    mActiveServices.add(
-                            new JobServiceContext(this, mConcurrencyManager, mBatteryStats,
-                                    mJobPackageTracker, getContext().getMainLooper()));
-                }
+                mConcurrencyManager.onThirdPartyAppsCanStart();
                 // Attach jobs to their controllers.
                 mJobs.forEachJob((job) -> {
                     for (int controller = 0; controller < mControllers.size(); controller++) {
@@ -1961,19 +1932,6 @@
         return removed;
     }
 
-    private boolean stopJobOnServiceContextLocked(JobStatus job,
-            @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
-        for (int i = 0; i < mActiveServices.size(); i++) {
-            JobServiceContext jsc = mActiveServices.get(i);
-            final JobStatus executing = jsc.getRunningJobLocked();
-            if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
-                jsc.cancelExecutingJobLocked(reason, internalReasonCode, debugReason);
-                return true;
-            }
-        }
-        return false;
-    }
-
     /** Return {@code true} if the specified job is currently executing. */
     @GuardedBy("mLock")
     public boolean isCurrentlyRunningLocked(JobStatus job) {
@@ -2383,7 +2341,8 @@
      * - if passes all the restrictions or has {@link JobInfo#BIAS_FOREGROUND_SERVICE} bias
      * or higher.
      */
-    private JobRestriction checkIfRestricted(JobStatus job) {
+    @GuardedBy("mLock")
+    JobRestriction checkIfRestricted(JobStatus job) {
         if (evaluateJobBiasLocked(job) >= JobInfo.BIAS_FOREGROUND_SERVICE) {
             // Jobs with BIAS_FOREGROUND_SERVICE or higher should not be restricted
             return null;
@@ -2397,38 +2356,9 @@
         return null;
     }
 
+    @GuardedBy("mLock")
     private void stopNonReadyActiveJobsLocked() {
-        for (int i=0; i<mActiveServices.size(); i++) {
-            JobServiceContext serviceContext = mActiveServices.get(i);
-            final JobStatus running = serviceContext.getRunningJobLocked();
-            if (running == null) {
-                continue;
-            }
-            if (!running.isReady()) {
-                if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX
-                        && running.getStopReason() == JobParameters.STOP_REASON_APP_STANDBY) {
-                    serviceContext.cancelExecutingJobLocked(
-                            running.getStopReason(),
-                            JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET,
-                            "cancelled due to restricted bucket");
-                } else {
-                    serviceContext.cancelExecutingJobLocked(
-                            running.getStopReason(),
-                            JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
-                            "cancelled due to unsatisfied constraints");
-                }
-            } else {
-                final JobRestriction restriction = checkIfRestricted(running);
-                if (restriction != null) {
-                    final int internalReasonCode = restriction.getInternalReason();
-                    serviceContext.cancelExecutingJobLocked(restriction.getReason(),
-                            internalReasonCode,
-                            "restricted due to "
-                                    + JobParameters.getInternalReasonCodeDescription(
-                                    internalReasonCode));
-                }
-            }
-        }
+        mConcurrencyManager.stopNonReadyActiveJobsLocked();
     }
 
     /**
@@ -2598,7 +2528,7 @@
                             debugReason = "couldn't figure out why the job should stop running";
                         }
                     }
-                    stopJobOnServiceContextLocked(job, job.getStopReason(),
+                    mConcurrencyManager.stopJobOnServiceContextLocked(job, job.getStopReason(),
                             internalStopReason, debugReason);
                 } else if (mPendingJobs.remove(job)) {
                     noteJobNonPending(job);
@@ -3516,9 +3446,11 @@
             final ArrayList<JobInfo> runningJobs;
 
             synchronized (mLock) {
-                runningJobs = new ArrayList<>(mActiveServices.size());
-                for (JobServiceContext jsc : mActiveServices) {
-                    final JobStatus job = jsc.getRunningJobLocked();
+                final ArraySet<JobStatus> runningJobStatuses =
+                        mConcurrencyManager.getRunningJobsLocked();
+                runningJobs = new ArrayList<>(runningJobStatuses.size());
+                for (int i = runningJobStatuses.size() - 1; i >= 0; --i) {
+                    final JobStatus job = runningJobStatuses.valueAt(i);
                     if (job != null) {
                         runningJobs.add(job.getJob());
                     }
@@ -3599,18 +3531,8 @@
         }
 
         synchronized (mLock) {
-            boolean foundSome = false;
-            for (int i = 0; i < mActiveServices.size(); i++) {
-                final JobServiceContext jc = mActiveServices.get(i);
-                final JobStatus js = jc.getRunningJobLocked();
-                if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
-                    foundSome = true;
-                    pw.print("Timing out: ");
-                    js.printUniqueId(pw);
-                    pw.print(" ");
-                    pw.println(js.getServiceComponent().flattenToShortString());
-                }
-            }
+            final boolean foundSome = mConcurrencyManager.executeTimeoutCommandLocked(pw,
+                    pkgName, userId, hasJobId, jobId);
             if (!foundSome) {
                 pw.println("No matching executing jobs found.");
             }
@@ -4037,38 +3959,7 @@
             pw.decreaseIndent();
 
             pw.println();
-            pw.println("Active jobs:");
-            pw.increaseIndent();
-            for (int i=0; i<mActiveServices.size(); i++) {
-                JobServiceContext jsc = mActiveServices.get(i);
-                final JobStatus job = jsc.getRunningJobLocked();
-
-                if (job != null && !predicate.test(job)) {
-                    continue;
-                }
-
-                pw.print("Slot #"); pw.print(i); pw.print(": ");
-                jsc.dumpLocked(pw, nowElapsed);
-
-                if (job != null) {
-                    pw.increaseIndent();
-
-                    pw.increaseIndent();
-                    job.dump(pw, false, nowElapsed);
-                    pw.decreaseIndent();
-
-                    pw.print("Evaluated bias: ");
-                    pw.println(JobInfo.getBiasString(job.lastEvaluatedBias));
-
-                    pw.print("Active at ");
-                    TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
-                    pw.print(", pending for ");
-                    TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
-                    pw.decreaseIndent();
-                    pw.println();
-                }
-            }
-            pw.decreaseIndent();
+            mConcurrencyManager.dumpActiveJobsLocked(pw, predicate, nowElapsed, nowUptime);
 
             pw.println();
             boolean recentPrinted = false;
@@ -4228,45 +4119,6 @@
 
                 proto.end(pjToken);
             }
-            for (JobServiceContext jsc : mActiveServices) {
-                final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
-                final JobStatus job = jsc.getRunningJobLocked();
-
-                if (job == null) {
-                    final long ijToken = proto.start(ActiveJob.INACTIVE);
-
-                    proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
-                            nowElapsed - jsc.mStoppedTime);
-                    if (jsc.mStoppedReason != null) {
-                        proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
-                                jsc.mStoppedReason);
-                    }
-
-                    proto.end(ijToken);
-                } else {
-                    final long rjToken = proto.start(ActiveJob.RUNNING);
-
-                    job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
-
-                    proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
-                            nowElapsed - jsc.getExecutionStartTimeElapsed());
-                    proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
-                            jsc.getTimeoutElapsed() - nowElapsed);
-
-                    job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
-
-                    proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY,
-                            evaluateJobBiasLocked(job));
-
-                    proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
-                            nowUptime - job.madeActive);
-                    proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
-                            job.madeActive - job.madePending);
-
-                    proto.end(rjToken);
-                }
-                proto.end(ajToken);
-            }
             if (filterUid == -1) {
                 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
                 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index efcf14f..30fdb1e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -441,11 +441,6 @@
     /** The reason a job most recently went from ready to not ready. */
     private int mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
 
-    /** Provide a handle to the service that this job will be run on. */
-    public int getServiceToken() {
-        return callingUid;
-    }
-
     /**
      * Core constructor for JobStatus instances.  All other ctors funnel down to this one.
      *
diff --git a/core/api/current.txt b/core/api/current.txt
index b5493a3..3f12b6c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5630,6 +5630,7 @@
     method public boolean onException(Object, Throwable);
     method public void onStart();
     method public void removeMonitor(android.app.Instrumentation.ActivityMonitor);
+    method public void resetInTouchMode();
     method public void runOnMainSync(Runnable);
     method public void sendCharacterSync(int);
     method public void sendKeyDownUpSync(int);
@@ -5814,6 +5815,7 @@
   public class LocaleManager {
     method @NonNull public android.os.LocaleList getApplicationLocales();
     method @NonNull @RequiresPermission(value="android.permission.READ_APP_SPECIFIC_LOCALES", conditional=true) public android.os.LocaleList getApplicationLocales(@NonNull String);
+    method @NonNull public android.os.LocaleList getSystemLocales();
     method public void setApplicationLocales(@NonNull android.os.LocaleList);
   }
 
@@ -6644,6 +6646,15 @@
 
   public final class PictureInPictureParams implements android.os.Parcelable {
     method public int describeContents();
+    method @NonNull public java.util.List<android.app.RemoteAction> getActions();
+    method @Nullable public android.util.Rational getAspectRatio();
+    method @Nullable public android.app.RemoteAction getCloseAction();
+    method @Nullable public android.util.Rational getExpandedAspectRatio();
+    method @Nullable public android.graphics.Rect getSourceRectHint();
+    method @Nullable public CharSequence getSubtitle();
+    method @Nullable public CharSequence getTitle();
+    method public boolean isAutoEnterEnabled();
+    method public boolean isSeamlessResizeEnabled();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.PictureInPictureParams> CREATOR;
   }
@@ -9726,8 +9737,8 @@
     method @Nullable public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void removeStickyBroadcast(@RequiresPermission android.content.Intent);
     method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void removeStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
-    method public void revokeOwnPermissionOnKill(@NonNull String);
-    method public void revokeOwnPermissionsOnKill(@NonNull java.util.Collection<java.lang.String>);
+    method public void revokeSelfPermissionOnKill(@NonNull String);
+    method public void revokeSelfPermissionsOnKill(@NonNull java.util.Collection<java.lang.String>);
     method public abstract void revokeUriPermission(android.net.Uri, int);
     method public abstract void revokeUriPermission(String, android.net.Uri, int);
     method public abstract void sendBroadcast(@RequiresPermission android.content.Intent);
@@ -10115,12 +10126,16 @@
     method @Nullable public long[] getLongArrayExtra(String);
     method public long getLongExtra(String, long);
     method @Nullable public String getPackage();
-    method @Nullable public android.os.Parcelable[] getParcelableArrayExtra(String);
-    method @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayListExtra(String);
-    method @Nullable public <T extends android.os.Parcelable> T getParcelableExtra(String);
+    method @Deprecated @Nullable public android.os.Parcelable[] getParcelableArrayExtra(String);
+    method @Nullable public <T> T[] getParcelableArrayExtra(@Nullable String, @NonNull Class<T>);
+    method @Deprecated @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayListExtra(String);
+    method @Nullable public <T> java.util.ArrayList<T> getParcelableArrayListExtra(@Nullable String, @NonNull Class<? extends T>);
+    method @Deprecated @Nullable public <T extends android.os.Parcelable> T getParcelableExtra(String);
+    method @Nullable public <T> T getParcelableExtra(@Nullable String, @NonNull Class<T>);
     method @Nullable public String getScheme();
     method @Nullable public android.content.Intent getSelector();
-    method @Nullable public java.io.Serializable getSerializableExtra(String);
+    method @Deprecated @Nullable public java.io.Serializable getSerializableExtra(String);
+    method @Nullable public <T extends java.io.Serializable> T getSerializableExtra(@Nullable String, @NonNull Class<T>);
     method @Nullable public short[] getShortArrayExtra(String);
     method public short getShortExtra(String, short);
     method @Nullable public android.graphics.Rect getSourceBounds();
@@ -19477,26 +19492,26 @@
     method @NonNull public static String convert(@FloatRange double, int);
     method @FloatRange public static double convert(@NonNull String);
     method public int describeContents();
-    method public static void distanceBetween(@FloatRange double, @FloatRange double, @FloatRange double, @FloatRange double, float[]);
-    method @FloatRange public float distanceTo(@NonNull android.location.Location);
-    method public void dump(@NonNull android.util.Printer, @Nullable String);
-    method @FloatRange public float getAccuracy();
+    method public static void distanceBetween(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, float[]);
+    method @FloatRange(from=0.0) public float distanceTo(@NonNull android.location.Location);
+    method @Deprecated public void dump(@NonNull android.util.Printer, @Nullable String);
+    method @FloatRange(from=0.0) public float getAccuracy();
     method @FloatRange public double getAltitude();
-    method @FloatRange(from=0.0f, to=360.0f, toInclusive=false) public float getBearing();
-    method @FloatRange public float getBearingAccuracyDegrees();
-    method @IntRange public long getElapsedRealtimeAgeMillis();
-    method @IntRange public long getElapsedRealtimeAgeMillis(@IntRange long);
-    method @IntRange public long getElapsedRealtimeMillis();
-    method @IntRange public long getElapsedRealtimeNanos();
-    method @FloatRange public double getElapsedRealtimeUncertaintyNanos();
+    method @FloatRange(from=0.0, to=360.0, toInclusive=false) public float getBearing();
+    method @FloatRange(from=0.0) public float getBearingAccuracyDegrees();
+    method @IntRange(from=0) public long getElapsedRealtimeAgeMillis();
+    method public long getElapsedRealtimeAgeMillis(@IntRange(from=0) long);
+    method @IntRange(from=0) public long getElapsedRealtimeMillis();
+    method @IntRange(from=0) public long getElapsedRealtimeNanos();
+    method @FloatRange(from=0.0) public double getElapsedRealtimeUncertaintyNanos();
     method @Nullable public android.os.Bundle getExtras();
-    method @FloatRange public double getLatitude();
-    method @FloatRange public double getLongitude();
+    method @FloatRange(from=-90.0, to=90.0) public double getLatitude();
+    method @FloatRange(from=-180.0, to=180.0) public double getLongitude();
     method @Nullable public String getProvider();
-    method @FloatRange public float getSpeed();
-    method @FloatRange public float getSpeedAccuracyMetersPerSecond();
-    method @IntRange public long getTime();
-    method @FloatRange public float getVerticalAccuracyMeters();
+    method @FloatRange(from=0.0) public float getSpeed();
+    method @FloatRange(from=0.0) public float getSpeedAccuracyMetersPerSecond();
+    method @IntRange(from=0) public long getTime();
+    method @FloatRange(from=0.0) public float getVerticalAccuracyMeters();
     method public boolean hasAccuracy();
     method public boolean hasAltitude();
     method public boolean hasBearing();
@@ -19518,21 +19533,21 @@
     method public void removeVerticalAccuracy();
     method public void reset();
     method public void set(@NonNull android.location.Location);
-    method public void setAccuracy(@FloatRange float);
+    method public void setAccuracy(@FloatRange(from=0.0) float);
     method public void setAltitude(@FloatRange double);
     method public void setBearing(@FloatRange(fromInclusive=false, toInclusive=false) float);
-    method public void setBearingAccuracyDegrees(@FloatRange float);
-    method public void setElapsedRealtimeNanos(@IntRange long);
-    method public void setElapsedRealtimeUncertaintyNanos(@FloatRange double);
+    method public void setBearingAccuracyDegrees(@FloatRange(from=0.0) float);
+    method public void setElapsedRealtimeNanos(@IntRange(from=0) long);
+    method public void setElapsedRealtimeUncertaintyNanos(@FloatRange(from=0.0) double);
     method public void setExtras(@Nullable android.os.Bundle);
-    method public void setLatitude(@FloatRange double);
-    method public void setLongitude(@FloatRange double);
+    method public void setLatitude(@FloatRange(from=-90.0, to=90.0) double);
+    method public void setLongitude(@FloatRange(from=-180.0, to=180.0) double);
     method public void setMock(boolean);
     method public void setProvider(@Nullable String);
-    method public void setSpeed(@FloatRange float);
-    method public void setSpeedAccuracyMetersPerSecond(@FloatRange float);
-    method public void setTime(@IntRange long);
-    method public void setVerticalAccuracyMeters(@FloatRange float);
+    method public void setSpeed(@FloatRange(from=0.0) float);
+    method public void setSpeedAccuracyMetersPerSecond(@FloatRange(from=0.0) float);
+    method public void setTime(@IntRange(from=0) long);
+    method public void setVerticalAccuracyMeters(@FloatRange(from=0.0) float);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.location.Location> CREATOR;
     field public static final int FORMAT_DEGREES = 0; // 0x0
@@ -22176,7 +22191,7 @@
     field public static final String KEY_AAC_DRC_OUTPUT_LOUDNESS = "aac-drc-output-loudness";
     field public static final String KEY_AAC_DRC_TARGET_REFERENCE_LEVEL = "aac-target-ref-level";
     field public static final String KEY_AAC_ENCODED_TARGET_LEVEL = "aac-encoded-target-level";
-    field public static final String KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT = "aac-max-output-channel_count";
+    field @Deprecated public static final String KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT = "aac-max-output-channel_count";
     field public static final String KEY_AAC_PROFILE = "aac-profile";
     field public static final String KEY_AAC_SBR_MODE = "aac-sbr-mode";
     field public static final String KEY_ALLOW_FRAME_DROP = "allow-frame-drop";
@@ -23538,17 +23553,24 @@
   }
 
   public class Spatializer {
+    method public void addOnHeadTrackerAvailableListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadTrackerAvailableListener);
     method public void addOnSpatializerStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerStateChangedListener);
     method public boolean canBeSpatialized(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioFormat);
     method public int getImmersiveAudioLevel();
     method public boolean isAvailable();
     method public boolean isEnabled();
+    method public boolean isHeadTrackerAvailable();
+    method public void removeOnHeadTrackerAvailableListener(@NonNull android.media.Spatializer.OnHeadTrackerAvailableListener);
     method public void removeOnSpatializerStateChangedListener(@NonNull android.media.Spatializer.OnSpatializerStateChangedListener);
     field public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1; // 0x1
     field public static final int SPATIALIZER_IMMERSIVE_LEVEL_NONE = 0; // 0x0
     field public static final int SPATIALIZER_IMMERSIVE_LEVEL_OTHER = -1; // 0xffffffff
   }
 
+  public static interface Spatializer.OnHeadTrackerAvailableListener {
+    method public void onHeadTrackerAvailableChanged(@NonNull android.media.Spatializer, boolean);
+  }
+
   public static interface Spatializer.OnSpatializerStateChangedListener {
     method public void onSpatializerAvailableChanged(@NonNull android.media.Spatializer, boolean);
     method public void onSpatializerEnabledChanged(@NonNull android.media.Spatializer, boolean);
@@ -25181,17 +25203,23 @@
   }
 
   public final class CommandRequest extends android.media.tv.BroadcastInfoRequest implements android.os.Parcelable {
-    ctor public CommandRequest(int, int, @NonNull String, @NonNull String, @NonNull String);
+    ctor public CommandRequest(int, int, @NonNull String, @NonNull String, @NonNull String, @NonNull String);
+    method @NonNull public String getArgumentType();
     method @NonNull public String getArguments();
     method @NonNull public String getName();
-    method @NonNull public String getNameSpace();
+    method @NonNull public String getNamespace();
+    field public static final String ARGUMENT_TYPE_JSON = "json";
+    field public static final String ARGUMENT_TYPE_XML = "xml";
     field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.CommandRequest> CREATOR;
   }
 
   public final class CommandResponse extends android.media.tv.BroadcastInfoResponse implements android.os.Parcelable {
-    ctor public CommandResponse(int, int, int, @Nullable String);
+    ctor public CommandResponse(int, int, int, @Nullable String, @NonNull String);
     method @Nullable public String getResponse();
+    method @NonNull public String getResponseType();
     field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.CommandResponse> CREATOR;
+    field public static final String RESPONSE_TYPE_JSON = "json";
+    field public static final String RESPONSE_TYPE_XML = "xml";
   }
 
   public final class DsmccRequest extends android.media.tv.BroadcastInfoRequest implements android.os.Parcelable {
@@ -25256,7 +25284,7 @@
     ctor public StreamEventResponse(int, int, int, int, long, @Nullable byte[]);
     method @Nullable public byte[] getData();
     method public int getEventId();
-    method public long getNpt();
+    method public long getNptMillis();
     field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.StreamEventResponse> CREATOR;
   }
 
@@ -25286,7 +25314,7 @@
 
   public final class TimelineResponse extends android.media.tv.BroadcastInfoResponse implements android.os.Parcelable {
     ctor public TimelineResponse(int, int, int, @Nullable String, int, int, long, long);
-    method @Nullable public String getSelector();
+    method @Nullable public android.net.Uri getSelector();
     method public long getTicks();
     method public int getUnitsPerSecond();
     method public int getUnitsPerTick();
@@ -26082,21 +26110,8 @@
     method @NonNull public android.media.tv.interactive.AppLinkInfo.Builder setUriScheme(@NonNull String);
   }
 
-  public final class TvInteractiveAppInfo implements android.os.Parcelable {
-    ctor public TvInteractiveAppInfo(@NonNull android.content.Context, @NonNull android.content.ComponentName);
-    method public int describeContents();
-    method @NonNull public String getId();
-    method @Nullable public android.content.pm.ServiceInfo getServiceInfo();
-    method @NonNull public int getSupportedTypes();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.interactive.TvInteractiveAppInfo> CREATOR;
-    field public static final int INTERACTIVE_APP_TYPE_ATSC = 2; // 0x2
-    field public static final int INTERACTIVE_APP_TYPE_GINGA = 4; // 0x4
-    field public static final int INTERACTIVE_APP_TYPE_HBBTV = 1; // 0x1
-  }
-
   public final class TvInteractiveAppManager {
-    method @NonNull public java.util.List<android.media.tv.interactive.TvInteractiveAppInfo> getTvInteractiveAppServiceList();
+    method @NonNull public java.util.List<android.media.tv.interactive.TvInteractiveAppServiceInfo> getTvInteractiveAppServiceList();
     method public void prepare(@NonNull String, int);
     method public void registerAppLinkInfo(@NonNull String, @NonNull android.media.tv.interactive.AppLinkInfo);
     method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.interactive.TvInteractiveAppManager.TvInteractiveAppCallback);
@@ -26219,6 +26234,19 @@
     method @CallSuper public void setVideoBounds(@NonNull android.graphics.Rect);
   }
 
+  public final class TvInteractiveAppServiceInfo implements android.os.Parcelable {
+    ctor public TvInteractiveAppServiceInfo(@NonNull android.content.Context, @NonNull android.content.ComponentName);
+    method public int describeContents();
+    method @NonNull public String getId();
+    method @Nullable public android.content.pm.ServiceInfo getServiceInfo();
+    method @NonNull public int getSupportedTypes();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.interactive.TvInteractiveAppServiceInfo> CREATOR;
+    field public static final int INTERACTIVE_APP_TYPE_ATSC = 2; // 0x2
+    field public static final int INTERACTIVE_APP_TYPE_GINGA = 4; // 0x4
+    field public static final int INTERACTIVE_APP_TYPE_HBBTV = 1; // 0x1
+  }
+
   public class TvInteractiveAppView extends android.view.ViewGroup {
     ctor public TvInteractiveAppView(@NonNull android.content.Context);
     ctor public TvInteractiveAppView(@NonNull android.content.Context, @Nullable android.util.AttributeSet);
@@ -43786,6 +43814,7 @@
     method public int getNetworkTypeBitmask();
     method public String getOperatorNumeric();
     method public String getPassword();
+    method public int getProfileId();
     method public int getProtocol();
     method @Deprecated public java.net.InetAddress getProxyAddress();
     method public String getProxyAddressAsString();
@@ -43793,6 +43822,7 @@
     method public int getRoamingProtocol();
     method public String getUser();
     method public boolean isEnabled();
+    method public boolean isPersistent();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final int AUTH_TYPE_CHAP = 2; // 0x2
     field public static final int AUTH_TYPE_NONE = 0; // 0x0
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 3d5232b..d2cc0f5 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -243,7 +243,8 @@
     method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public android.telephony.SubscriptionPlan getSubscriptionPlan(@NonNull android.net.NetworkTemplate);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int);
-    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyStatsProviderWarningOrLimitReached();
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyStatsProviderLimitReached();
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyStatsProviderWarningReached();
     method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback);
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5da1f39..d094f4b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -244,7 +244,6 @@
     field public static final String READ_APP_SPECIFIC_LOCALES = "android.permission.READ_APP_SPECIFIC_LOCALES";
     field public static final String READ_CARRIER_APP_INFO = "android.permission.READ_CARRIER_APP_INFO";
     field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
-    field public static final String READ_CLIPBOARD_IN_BACKGROUND = "android.permission.READ_CLIPBOARD_IN_BACKGROUND";
     field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
     field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
     field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
@@ -1115,6 +1114,7 @@
     method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void setStrings(@NonNull java.util.Set<android.app.admin.DevicePolicyStringResource>);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification();
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
     field public static final String ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE = "android.app.action.BIND_SECONDARY_LOCKSCREEN_SERVICE";
@@ -5490,6 +5490,32 @@
     method @NonNull public android.location.GnssCapabilities.Builder setHasSatellitePvt(boolean);
   }
 
+  public final class GnssExcessPathInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @FloatRange(from=0.0f) public float getAttenuationDb();
+    method @FloatRange(from=0.0f) public float getExcessPathLengthMeters();
+    method @FloatRange(from=0.0f) public float getExcessPathLengthUncertaintyMeters();
+    method @NonNull public android.location.GnssReflectingPlane getReflectingPlane();
+    method public boolean hasAttenuation();
+    method public boolean hasExcessPathLength();
+    method public boolean hasExcessPathLengthUncertainty();
+    method public boolean hasReflectingPlane();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssExcessPathInfo> CREATOR;
+  }
+
+  public static final class GnssExcessPathInfo.Builder {
+    ctor public GnssExcessPathInfo.Builder();
+    method @NonNull public android.location.GnssExcessPathInfo build();
+    method @NonNull public android.location.GnssExcessPathInfo.Builder clearAttenuationDb();
+    method @NonNull public android.location.GnssExcessPathInfo.Builder clearExcessPathLengthMeters();
+    method @NonNull public android.location.GnssExcessPathInfo.Builder clearExcessPathLengthUncertaintyMeters();
+    method @NonNull public android.location.GnssExcessPathInfo.Builder setAttenuationDb(@FloatRange(from=0.0f) float);
+    method @NonNull public android.location.GnssExcessPathInfo.Builder setExcessPathLengthMeters(@FloatRange(from=0.0f) float);
+    method @NonNull public android.location.GnssExcessPathInfo.Builder setExcessPathLengthUncertaintyMeters(@FloatRange(from=0.0f) float);
+    method @NonNull public android.location.GnssExcessPathInfo.Builder setReflectingPlane(@Nullable android.location.GnssReflectingPlane);
+  }
+
   public final class GnssMeasurement implements android.os.Parcelable {
     method @Nullable public java.util.Collection<android.location.CorrelationVector> getCorrelationVectors();
     method @Nullable public android.location.SatellitePvt getSatellitePvt();
@@ -5571,15 +5597,18 @@
   public final class GnssSingleSatCorrection implements android.os.Parcelable {
     method public int describeContents();
     method @FloatRange(from=0.0f, fromInclusive=false) public float getCarrierFrequencyHz();
+    method @FloatRange(from=0.0f) public float getCombinedAttenuationDb();
     method public int getConstellationType();
     method @FloatRange(from=0.0f) public float getExcessPathLengthMeters();
     method @FloatRange(from=0.0f) public float getExcessPathLengthUncertaintyMeters();
+    method @NonNull public java.util.List<android.location.GnssExcessPathInfo> getGnssExcessPathInfoList();
     method @FloatRange(from=0.0f, to=1.0f) public float getProbabilityLineOfSight();
-    method @Nullable public android.location.GnssReflectingPlane getReflectingPlane();
+    method @Deprecated @Nullable public android.location.GnssReflectingPlane getReflectingPlane();
     method @IntRange(from=0) public int getSatelliteId();
+    method public boolean hasCombinedAttenuation();
     method public boolean hasExcessPathLength();
     method public boolean hasExcessPathLengthUncertainty();
-    method public boolean hasReflectingPlane();
+    method @Deprecated public boolean hasReflectingPlane();
     method public boolean hasValidSatelliteLineOfSight();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GnssSingleSatCorrection> CREATOR;
@@ -5588,15 +5617,18 @@
   public static final class GnssSingleSatCorrection.Builder {
     ctor public GnssSingleSatCorrection.Builder();
     method @NonNull public android.location.GnssSingleSatCorrection build();
+    method @NonNull public android.location.GnssSingleSatCorrection.Builder clearCombinedAttenuationDb();
     method @NonNull public android.location.GnssSingleSatCorrection.Builder clearExcessPathLengthMeters();
     method @NonNull public android.location.GnssSingleSatCorrection.Builder clearExcessPathLengthUncertaintyMeters();
     method @NonNull public android.location.GnssSingleSatCorrection.Builder clearProbabilityLineOfSight();
     method @NonNull public android.location.GnssSingleSatCorrection.Builder setCarrierFrequencyHz(@FloatRange(from=0.0f, fromInclusive=false) float);
+    method @NonNull public android.location.GnssSingleSatCorrection.Builder setCombinedAttenuationDb(@FloatRange(from=0.0f) float);
     method @NonNull public android.location.GnssSingleSatCorrection.Builder setConstellationType(int);
     method @NonNull public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthMeters(@FloatRange(from=0.0f) float);
     method @NonNull public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthUncertaintyMeters(@FloatRange(from=0.0f) float);
+    method @NonNull public android.location.GnssSingleSatCorrection.Builder setGnssExcessPathInfoList(@NonNull java.util.List<android.location.GnssExcessPathInfo>);
     method @NonNull public android.location.GnssSingleSatCorrection.Builder setProbabilityLineOfSight(@FloatRange(from=0.0f, to=1.0f) float);
-    method @NonNull public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(@Nullable android.location.GnssReflectingPlane);
+    method @Deprecated @NonNull public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(@Nullable android.location.GnssReflectingPlane);
     method @NonNull public android.location.GnssSingleSatCorrection.Builder setSatelliteId(@IntRange(from=0) int);
   }
 
@@ -10049,9 +10081,9 @@
     method @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
     method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
-    method @BinderThread public void onRevokeOwnPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
+    method @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
     method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
@@ -10865,10 +10897,11 @@
   }
 
   public static final class AmbientContextDetectionResult.Builder {
-    ctor public AmbientContextDetectionResult.Builder();
+    ctor public AmbientContextDetectionResult.Builder(@NonNull String);
     method @NonNull public android.service.ambientcontext.AmbientContextDetectionResult.Builder addEvent(@NonNull android.app.ambientcontext.AmbientContextEvent);
+    method @NonNull public android.service.ambientcontext.AmbientContextDetectionResult.Builder addEvents(@NonNull java.util.List<android.app.ambientcontext.AmbientContextEvent>);
     method @NonNull public android.service.ambientcontext.AmbientContextDetectionResult build();
-    method @NonNull public android.service.ambientcontext.AmbientContextDetectionResult.Builder setPackageName(@NonNull String);
+    method @NonNull public android.service.ambientcontext.AmbientContextDetectionResult.Builder clearEvents();
   }
 
   public abstract class AmbientContextDetectionService extends android.app.Service {
@@ -10889,9 +10922,8 @@
   }
 
   public static final class AmbientContextDetectionServiceStatus.Builder {
-    ctor public AmbientContextDetectionServiceStatus.Builder();
+    ctor public AmbientContextDetectionServiceStatus.Builder(@NonNull String);
     method @NonNull public android.service.ambientcontext.AmbientContextDetectionServiceStatus build();
-    method @NonNull public android.service.ambientcontext.AmbientContextDetectionServiceStatus.Builder setPackageName(@NonNull String);
     method @NonNull public android.service.ambientcontext.AmbientContextDetectionServiceStatus.Builder setStatusCode(int);
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index db95a1f..d984abe 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -300,7 +300,6 @@
   }
 
   public class LocaleManager {
-    method @Nullable public android.os.LocaleList getSystemLocales();
     method public void setSystemLocales(@NonNull android.os.LocaleList);
   }
 
@@ -356,14 +355,8 @@
   }
 
   public final class PictureInPictureParams implements android.os.Parcelable {
-    method public java.util.List<android.app.RemoteAction> getActions();
-    method public float getAspectRatio();
-    method @Nullable public android.app.RemoteAction getCloseAction();
-    method public float getExpandedAspectRatio();
-    method public android.graphics.Rect getSourceRectHint();
-    method @Nullable public CharSequence getSubtitle();
-    method @Nullable public CharSequence getTitle();
-    method public boolean isSeamlessResizeEnabled();
+    method public float getAspectRatioFloat();
+    method public float getExpandedAspectRatioFloat();
   }
 
   public final class PictureInPictureUiState implements android.os.Parcelable {
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index 4e6cfb35..f53cfe4 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -169,10 +169,6 @@
             mIntroResId = asAttributes.getResourceId(
                     com.android.internal.R.styleable.AccessibilityShortcutTarget_intro, 0);
             asAttributes.recycle();
-
-            if ((mDescriptionResId == 0 && mHtmlDescriptionRes == 0) || mSummaryResId == 0) {
-                throw new XmlPullParserException("No description or summary in meta-data");
-            }
         } catch (PackageManager.NameNotFoundException e) {
             throw new XmlPullParserException("Unable to create context for: "
                     + mActivityInfo.packageName);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f5eb1f6..ac46066 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -77,6 +77,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
+import android.permission.PermissionControllerManager;
 import android.permission.PermissionManager;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -216,6 +217,12 @@
     @UnsupportedAppUsage
     private @Nullable ClassLoader mClassLoader;
 
+    /**
+     * The {@link com.android.server.wm.WindowToken} representing this instance if it is
+     * {@link #CONTEXT_TYPE_WINDOW_CONTEXT} or {@link #CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI}.
+     * If the type is {@link #CONTEXT_TYPE_ACTIVITY}, then represents the
+     * {@link android.window.WindowContainerToken} of the activity.
+     */
     private final @Nullable IBinder mToken;
 
     private final @NonNull UserHandle mUser;
@@ -2180,8 +2187,9 @@
     }
 
     @Override
-    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
-        getSystemService(PermissionManager.class).revokeOwnPermissionsOnKill(permissions);
+    public void revokeSelfPermissionsOnKill(@NonNull Collection<String> permissions) {
+        getSystemService(PermissionControllerManager.class).revokeSelfPermissionsOnKill(
+                getPackageName(), new ArrayList<String>(permissions));
     }
 
     @Override
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 6f49c9e..a138fa1 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -191,7 +191,7 @@
      */
     @TestApi
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
-    public @GameMode boolean isAngleEnabled(@NonNull String packageName) {
+    public boolean isAngleEnabled(@NonNull String packageName) {
         try {
             return mService.isAngleEnabled(packageName, mContext.getUserId());
         } catch (RemoteException e) {
diff --git a/core/java/android/app/ILocaleManager.aidl b/core/java/android/app/ILocaleManager.aidl
index 348cb2d..3002c8b 100644
--- a/core/java/android/app/ILocaleManager.aidl
+++ b/core/java/android/app/ILocaleManager.aidl
@@ -40,4 +40,9 @@
       */
      LocaleList getApplicationLocales(String packageName, int userId);
 
+     /**
+       * Returns the current system locales.
+       */
+     LocaleList getSystemLocales();
+
  }
\ No newline at end of file
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e0c69df..ac979c4 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -393,6 +393,15 @@
     }
 
     /**
+     * Resets the {@link #setInTouchMode touch mode} to the device default.
+     */
+    public void resetInTouchMode() {
+        final boolean defaultInTouchMode = getContext().getResources().getBoolean(
+                com.android.internal.R.bool.config_defaultInTouchMode);
+        setInTouchMode(defaultInTouchMode);
+    }
+
+    /**
      * Schedule a callback for when the application's main thread goes idle
      * (has no more events to process).
      *
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index cedf483e..e9c29b8 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -1103,9 +1103,12 @@
     }
 
     /**
-     * Registers a listener to execute when the keyguard visibility changes.
+     * Registers a listener to execute when the keyguard locked state changes.
      *
-     * @param listener The listener to add to receive keyguard visibility changes.
+     * @param listener The listener to add to receive keyguard locked state changes.
+     *
+     * @see #isKeyguardLocked()
+     * @see #removeKeyguardLockedStateListener(KeyguardLockedStateListener)
      */
     @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
     public void addKeyguardLockedStateListener(@NonNull @CallbackExecutor Executor executor,
@@ -1124,7 +1127,12 @@
     }
 
     /**
-     * Unregisters a listener that executes when the keyguard visibility changes.
+     * Unregisters a listener that executes when the keyguard locked state changes.
+     *
+     * @param listener The listener to remove.
+     *
+     * @see #isKeyguardLocked()
+     * @see #addKeyguardLockedStateListener(Executor, KeyguardLockedStateListener)
      */
     @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
     public void removeKeyguardLockedStateListener(@NonNull KeyguardLockedStateListener listener) {
diff --git a/core/java/android/app/LocaleManager.java b/core/java/android/app/LocaleManager.java
index 522dc84..efe9e35 100644
--- a/core/java/android/app/LocaleManager.java
+++ b/core/java/android/app/LocaleManager.java
@@ -18,7 +18,6 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -127,6 +126,26 @@
     }
 
     /**
+     * Returns the current system locales, ignoring app-specific overrides.
+     *
+     * <p><b>Note:</b> Apps should generally access the user's locale preferences as indicated in
+     * their in-process {@link LocaleList}s. However, in case an app-specific locale is set, this
+     * method helps cater to rare use-cases which might require specifically knowing the system
+     * locale.
+     *
+     * <p><b>Note:</b> This API is not user-aware. It returns the system locales for the foreground
+     * user.
+     */
+    @NonNull
+    public LocaleList getSystemLocales() {
+        try {
+            return mService.getSystemLocales();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Sets the current system locales to the provided value.
      *
      * @hide
@@ -142,19 +161,4 @@
         }
     }
 
-    /**
-     * Returns the current system locales for the device.
-     *
-     * @hide
-     */
-    @TestApi
-    @Nullable
-    public LocaleList getSystemLocales() {
-        try {
-            return ActivityManager.getService().getConfiguration().getLocales();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
 }
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index 2d2788c..3f1844e 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -280,7 +280,7 @@
     private Rational mAspectRatio;
 
     /**
-     * The expected aspect ratio of the vertically expanded picture-in-picture window.
+     * The expected aspect ratio of the expanded picture-in-picture window.
      */
     @Nullable
     private Rational mExpandedAspectRatio;
@@ -441,15 +441,21 @@
      * @hide
      */
     @TestApi
-    public float getAspectRatio() {
+    public float getAspectRatioFloat() {
         if (mAspectRatio != null) {
             return mAspectRatio.floatValue();
         }
         return 0f;
     }
 
-    /** @hide */
-    public Rational getAspectRatioRational() {
+    /**
+     * Returns the expected aspect ratio of the picture-in-picture window.
+     *
+     * @return aspect ratio as the desired width / height or {@code null} if not set.
+     * @see PictureInPictureParams.Builder#setAspectRatio(Rational)
+     */
+    @Nullable
+    public Rational getAspectRatio() {
         return mAspectRatio;
     }
 
@@ -466,7 +472,7 @@
      * @hide
      */
     @TestApi
-    public float getExpandedAspectRatio() {
+    public float getExpandedAspectRatioFloat() {
         if (mExpandedAspectRatio != null) {
             return mExpandedAspectRatio.floatValue();
         }
@@ -474,6 +480,17 @@
     }
 
     /**
+     * Returns the desired aspect ratio of the expanded picture-in-picture window.
+     *
+     * @return aspect ratio as the desired width / height or {@code null} if not set.
+     * @see PictureInPictureParams.Builder#setExpandedAspectRatio(Rational)
+     */
+    @Nullable
+    public Rational getExpandedAspectRatio() {
+        return mExpandedAspectRatio;
+    }
+
+    /**
      * @return whether the expanded aspect ratio is set
      * @hide
      */
@@ -482,11 +499,17 @@
     }
 
     /**
-     * @return the set of user actions.
-     * @hide
+     * Returns the list of user actions that are associated with the activity when in
+     * picture-in-picture mode.
+     *
+     * @return the user actions in a new list.
+     * @see PictureInPictureParams.Builder#setActions(List)
      */
-    @TestApi
+    @NonNull
     public List<RemoteAction> getActions() {
+        if (mUserActions == null) {
+            return new ArrayList<>();
+        }
         return mUserActions;
     }
 
@@ -499,10 +522,11 @@
     }
 
     /**
-     * @return the close action.
-     * @hide
+     * Returns the action that is to replace the system close action.
+     *
+     * @return the close action or {@code null} if not set.
+     * @see PictureInPictureParams.Builder#setCloseAction(RemoteAction)
      */
-    @TestApi
     @Nullable
     public RemoteAction getCloseAction() {
         return mCloseAction;
@@ -528,10 +552,12 @@
     }
 
     /**
-     * @return the source rect hint
-     * @hide
+     * Returns the source rect hint.
+     *
+     * @return the source rect hint also known as launch bounds or {@code null} if not set.
+     * @see PictureInPictureParams.Builder#setSourceRectHint(Rect)
      */
-    @TestApi
+    @Nullable
     public Rect getSourceRectHint() {
         return mSourceRectHint;
     }
@@ -545,18 +571,23 @@
     }
 
     /**
-     * @return whether auto pip is enabled.
-     * @hide
+     * Returns whether auto enter picture-in-picture is enabled.
+     *
+     * @return {@code true} if the system will automatically put the activity in
+     * picture-in-picture mode.
+     * @see PictureInPictureParams.Builder#setAutoEnterEnabled(boolean)
      */
     public boolean isAutoEnterEnabled() {
         return mAutoEnterEnabled == null ? false : mAutoEnterEnabled;
     }
 
     /**
-     * @return whether seamless resize is enabled.
-     * @hide
+     * Returns whether seamless resize is enabled.
+     *
+     * @return true if the system can seamlessly resize the window while activity is in
+     * picture-in-picture mode.
+     * @see PictureInPictureParams.Builder#setSeamlessResizeEnabled(boolean)
      */
-    @TestApi
     public boolean isSeamlessResizeEnabled() {
         return mSeamlessResizeEnabled == null ? true : mSeamlessResizeEnabled;
     }
@@ -570,10 +601,11 @@
     }
 
     /**
-     * @return title of the pip.
-     * @hide
+     * Returns the title of the picture-in-picture window that may be displayed to the user.
+     *
+     * @return title of the picture-in-picture window.
+     * @see PictureInPictureParams.Builder#setTitle(CharSequence)
      */
-    @TestApi
     @Nullable
     public CharSequence getTitle() {
         return mTitle;
@@ -588,10 +620,11 @@
     }
 
     /**
-     * @return subtitle of the pip.
-     * @hide
+     * Returns the subtitle of the picture-in-picture window that may be displayed to the user.
+     *
+     * @return subtitle of the picture-in-picture window.
+     * @see PictureInPictureParams.Builder#setSubtitle(CharSequence)
      */
-    @TestApi
     @Nullable
     public CharSequence getSubtitle() {
         return mSubtitle;
@@ -716,7 +749,7 @@
     @Override
     public String toString() {
         return "PictureInPictureParams("
-                + " aspectRatio=" + getAspectRatioRational()
+                + " aspectRatio=" + getAspectRatio()
                 + " expandedAspectRatio=" + mExpandedAspectRatio
                 + " sourceRectHint=" + getSourceRectHint()
                 + " hasSetActions=" + hasSetActions()
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 150888c..89854bb 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -24,9 +24,6 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.app.compat.CompatChanges;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -42,7 +39,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.util.Pair;
 import android.util.Slog;
 import android.view.View;
@@ -524,27 +520,6 @@
     private final Map<NearbyMediaDevicesProvider, NearbyMediaDevicesProviderWrapper>
             nearbyMediaDevicesProviderMap = new HashMap<>();
 
-    /**
-     * Media controls based on {@link android.app.Notification.MediaStyle} notifications will have
-     * actions based on the media session's {@link android.media.session.PlaybackState}, rather than
-     * the notification's actions.
-     *
-     * These actions will be:
-     * - Play/Pause (depending on whether the current state is a playing state)
-     * - Previous (if declared), or a custom action if the slot is not reserved with
-     *   {@code SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV}
-     * - Next (if declared), or a custom action if the slot is not reserved with
-     *   {@code SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT}
-     * - Custom action
-     * - Custom action
-     *
-     * @see androidx.media.utils.MediaConstants#SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV
-     * @see androidx.media.utils.MediaConstants#SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT
-     */
-    @ChangeId
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
-    private static final long MEDIA_CONTROL_SESSION_ACTIONS = 203800354L;
-
     @UnsupportedAppUsage
     private Context mContext;
     private IStatusBarService mService;
@@ -1152,20 +1127,6 @@
         }
     }
 
-    /**
-     * Checks whether the given package should use session-based actions for its media controls.
-     *
-     * @param packageName App posting media controls
-     * @param userId Current user ID
-     * @return true if the app supports session actions
-     *
-     * @hide
-     */
-    public static boolean useMediaSessionActionsForApp(String packageName, int userId) {
-        UserHandle handle = UserHandle.getUserHandleForUid(userId);
-        return CompatChanges.isChangeEnabled(MEDIA_CONTROL_SESSION_ACTIONS, packageName, handle);
-    }
-
     /** @hide */
     public static String windowStateToString(int state) {
         if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 27fe312..7269b0d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -15765,4 +15765,23 @@
         }
         return deviceManagerConfig;
     }
+
+    /**
+     * @return {@code true} if bypassing the device policy management role qualification is allowed
+     * with the current state of the device.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)
+    public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification() {
+        if (mService != null) {
+            try {
+                return mService.shouldAllowBypassingDevicePolicyManagementRoleQualification();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
index 40ae1f0..7e95177 100644
--- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
@@ -25,6 +25,7 @@
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
@@ -254,4 +255,18 @@
         return !mFactoryResetProtectionAccounts.isEmpty() && mFactoryResetProtectionEnabled;
     }
 
+    /**
+     * @hide
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.print("factoryResetProtectionEnabled=");
+        pw.println(mFactoryResetProtectionEnabled);
+
+        pw.print("factoryResetProtectionAccounts=");
+        pw.increaseIndent();
+        for (int i = 0; i < mFactoryResetProtectionAccounts.size(); i++) {
+            pw.println(mFactoryResetProtectionAccounts.get(i));
+        }
+        pw.decreaseIndent();
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 77db146..fb1ca41 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -558,4 +558,6 @@
     void setStrings(in List<DevicePolicyStringResource> strings);
     void resetStrings(in String[] stringIds);
     ParcelableResource getString(String stringId);
+
+    boolean shouldAllowBypassingDevicePolicyManagementRoleQualification();
 }
diff --git a/core/java/android/app/usage/BroadcastResponseStats.java b/core/java/android/app/usage/BroadcastResponseStats.java
index e1d37e1..572c453 100644
--- a/core/java/android/app/usage/BroadcastResponseStats.java
+++ b/core/java/android/app/usage/BroadcastResponseStats.java
@@ -29,6 +29,8 @@
  * Class containing a collection of stats related to response events started from an app
  * after receiving a broadcast.
  *
+ * @see UsageStatsManager#queryBroadcastResponseStats(String, long)
+ * @see UsageStatsManager#clearBroadcastResponseStats(String, long)
  * @hide
  */
 @SystemApi
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 60efb4d..24b1b6a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6508,22 +6508,22 @@
 
 
     /**
-     * Triggers the asynchronous revocation of a permission.
+     * Triggers the asynchronous revocation of a runtime permission. If the permission is not
+     * currently granted, nothing happens (even if later granted by the user).
      *
      * @param permName The name of the permission to be revoked.
-     * @see #revokeOwnPermissionsOnKill(Collection)
+     * @see #revokeSelfPermissionsOnKill(Collection)
+     * @throws IllegalArgumentException if the permission is not a runtime permission
      */
-    public void revokeOwnPermissionOnKill(@NonNull String permName) {
-        revokeOwnPermissionsOnKill(Collections.singletonList(permName));
+    public void revokeSelfPermissionOnKill(@NonNull String permName) {
+        revokeSelfPermissionsOnKill(Collections.singletonList(permName));
     }
 
     /**
      * Triggers the revocation of one or more permissions for the calling package. A package is only
-     * able to revoke a permission under the following conditions:
-     * <ul>
-     * <li>Each permission in {@code permissions} must be granted to the calling package.
-     * <li>Each permission in {@code permissions} must be a runtime permission.
-     * </ul>
+     * able to revoke runtime permissions. If a permission is not currently granted, it is ignored
+     * and will not get revoked (even if later granted by the user). Ultimately, you should never
+     * make assumptions about a permission status as users may grant or revoke them at any time.
      * <p>
      * Background permissions which have no corresponding foreground permission still granted once
      * the revocation is effective will also be revoked.
@@ -6549,8 +6549,9 @@
      * @param permissions Collection of permissions to be revoked.
      * @see PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer)
      * @see PackageManager#getPlatformPermissionsForGroup(String, Executor, Consumer)
+     * @throws IllegalArgumentException if any of the permissions is not a runtime permission
      */
-    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
+    public void revokeSelfPermissionsOnKill(@NonNull Collection<String> permissions) {
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
 
@@ -7145,8 +7146,9 @@
     }
 
     /**
-     * Returns token if the {@link Context} is a {@link android.app.WindowContext}. Returns
-     * {@code null} otherwise.
+     * Returns the {@link IBinder} representing the associated
+     * {@link com.android.server.wm.WindowToken} if the {@link Context} is a
+     * {@link android.app.WindowContext}. Returns {@code null} otherwise.
      *
      * @hide
      */
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 9adf173..4ecd776 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1036,8 +1036,8 @@
     }
 
     @Override
-    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
-        mBase.revokeOwnPermissionsOnKill(permissions);
+    public void revokeSelfPermissionsOnKill(@NonNull Collection<String> permissions) {
+        mBase.revokeSelfPermissionsOnKill(permissions);
     }
 
     @Override
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 478befd..2c207bc 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -8903,8 +8903,12 @@
      * @return the value of an item previously added with putExtra(),
      * or null if no Parcelable value was found.
      *
+     * @deprecated Use the type-safer {@link #getParcelableExtra(String, Class)} starting from
+     *      Android {@link Build.VERSION_CODES#TIRAMISU}.
+     *
      * @see #putExtra(String, Parcelable)
      */
+    @Deprecated
     public @Nullable <T extends Parcelable> T getParcelableExtra(String name) {
         return mExtras == null ? null : mExtras.<T>getParcelable(name);
     }
@@ -8913,12 +8917,31 @@
      * Retrieve extended data from the intent.
      *
      * @param name The name of the desired item.
+     * @param clazz The type of the object expected.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no Parcelable value was found.
+     *
+     * @see #putExtra(String, Parcelable)
+     */
+    public @Nullable <T> T getParcelableExtra(@Nullable String name, @NonNull Class<T> clazz) {
+        return mExtras == null ? null : mExtras.getParcelable(name, clazz);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
      *
      * @return the value of an item previously added with putExtra(),
      * or null if no Parcelable[] value was found.
      *
+     * @deprecated Use the type-safer {@link #getParcelableArrayExtra(String, Class)} starting from
+     *      Android {@link Build.VERSION_CODES#TIRAMISU}.
+     *
      * @see #putExtra(String, Parcelable[])
      */
+    @Deprecated
     public @Nullable Parcelable[] getParcelableArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getParcelableArray(name);
     }
@@ -8927,13 +8950,34 @@
      * Retrieve extended data from the intent.
      *
      * @param name The name of the desired item.
+     * @param clazz The type of the items inside the array. This is only verified when unparceling.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no Parcelable[] value was found.
+     *
+     * @see #putExtra(String, Parcelable[])
+     */
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    public @Nullable <T> T[] getParcelableArrayExtra(@Nullable String name,
+            @NonNull Class<T> clazz) {
+        return mExtras == null ? null : mExtras.getParcelableArray(name, clazz);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
      *
      * @return the value of an item previously added with
      * putParcelableArrayListExtra(), or null if no
      * ArrayList<Parcelable> value was found.
      *
+     * @deprecated Use the type-safer {@link #getParcelableArrayListExtra(String, Class)} starting
+     *      from Android {@link Build.VERSION_CODES#TIRAMISU}.
+     *
      * @see #putParcelableArrayListExtra(String, ArrayList)
      */
+    @Deprecated
     public @Nullable <T extends Parcelable> ArrayList<T> getParcelableArrayListExtra(String name) {
         return mExtras == null ? null : mExtras.<T>getParcelableArrayList(name);
     }
@@ -8942,10 +8986,32 @@
      * Retrieve extended data from the intent.
      *
      * @param name The name of the desired item.
+     * @param clazz The type of the items inside the array list. This is only verified when
+     *     unparceling.
+     *
+     * @return the value of an item previously added with
+     * putParcelableArrayListExtra(), or null if no
+     * ArrayList<Parcelable> value was found.
+     *
+     * @see #putParcelableArrayListExtra(String, ArrayList)
+     */
+    @SuppressLint({"ConcreteCollection", "NullableCollection"})
+    public @Nullable <T> ArrayList<T> getParcelableArrayListExtra(@Nullable String name,
+            @NonNull Class<? extends T> clazz) {
+        return mExtras == null ? null : mExtras.<T>getParcelableArrayList(name, clazz);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
      *
      * @return the value of an item previously added with putExtra(),
      * or null if no Serializable value was found.
      *
+     * @deprecated Use the type-safer {@link #getSerializableExtra(String, Class)} starting from
+     *      Android {@link Build.VERSION_CODES#TIRAMISU}.
+     *
      * @see #putExtra(String, Serializable)
      */
     public @Nullable Serializable getSerializableExtra(String name) {
@@ -8956,6 +9022,22 @@
      * Retrieve extended data from the intent.
      *
      * @param name The name of the desired item.
+     * @param clazz The type of the object expected.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no Serializable value was found.
+     *
+     * @see #putExtra(String, Serializable)
+     */
+    public @Nullable <T extends Serializable> T getSerializableExtra(@Nullable String name,
+            @NonNull Class<T> clazz) {
+        return mExtras == null ? null : mExtras.getSerializable(name, clazz);
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * @param name The name of the desired item.
      *
      * @return the value of an item previously added with
      * putIntegerArrayListExtra(), or null if no
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index a05f5c9..c8bbb0c1 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -79,6 +79,13 @@
     @GuardedBy("sSync") private static ArraySet<ApkAssets> sSystemApkAssetsSet;
 
     /**
+     * Cookie value to use when the actual cookie is unknown. This value tells the system to search
+     * all the ApkAssets for the asset.
+     * @hide
+     */
+    public static final int COOKIE_UNKNOWN = -1;
+
+    /**
      * Mode for {@link #open(String, int)}: no specific information about how
      * data will be accessed.
      */
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 0f9075b..03d1151 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -294,8 +294,8 @@
                 dest.setTouchableInsets(
                         ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
 
-                // TODO(b/205803355): See if we can use View#OnLayoutChangeListener().
-                // TODO(b/205803355): See if we can replace DecorView#mNavigationColorViewState.view
+                // TODO(b/215443343): See if we can use View#OnLayoutChangeListener().
+                // TODO(b/215443343): See if we can replace DecorView#mNavigationColorViewState.view
                 boolean zOrderChanged = false;
                 if (decor instanceof ViewGroup) {
                     ViewGroup decorGroup = (ViewGroup) decor;
diff --git a/core/java/android/inputmethodservice/navigationbar/DeadZone.java b/core/java/android/inputmethodservice/navigationbar/DeadZone.java
index 4cfd813..382b6b0 100644
--- a/core/java/android/inputmethodservice/navigationbar/DeadZone.java
+++ b/core/java/android/inputmethodservice/navigationbar/DeadZone.java
@@ -148,7 +148,7 @@
             if (DEBUG) {
                 Log.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
             }
-            //TODO(b/205803355): call mNavBarController.touchAutoDim(mDisplayId); here
+            //TODO(b/215443343): call mNavBarController.touchAutoDim(mDisplayId); here
             int size = (int) getSize(event.getEventTime());
             // In the vertical orientation consume taps along the left edge.
             // In horizontal orientation consume taps along the top edge.
diff --git a/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java b/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java
index cfdb6ca..92d358f 100644
--- a/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java
+++ b/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java
@@ -89,7 +89,7 @@
     public KeyButtonView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        // TODO(b/205803355): Figure out better place to set this.
+        // TODO(b/215443343): Figure out better place to set this.
         switch (getId()) {
             case com.android.internal.R.id.input_method_nav_back:
                 mCode = KEYCODE_BACK;
@@ -285,11 +285,11 @@
     private void sendEvent(int action, int flags, long when) {
         if (mCode == KeyEvent.KEYCODE_BACK && flags != KeyEvent.FLAG_LONG_PRESS) {
             if (action == MotionEvent.ACTION_UP) {
-                // TODO(b/205803355): Implement notifyBackAction();
+                // TODO(b/215443343): Implement notifyBackAction();
             }
         }
 
-        // TODO(b/205803355): Consolidate this logic to somewhere else.
+        // TODO(b/215443343): Consolidate this logic to somewhere else.
         if (mContext instanceof InputMethodService) {
             final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
             final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 18ec8f5..2c2a703 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -580,6 +580,24 @@
     }
 
     /**
+     * Notifies that the specified {@link NetworkStatsProvider} has reached its warning threshold
+     * which was set through {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void notifyStatsProviderWarningReached() {
+        try {
+            mService.notifyStatsProviderWarningOrLimitReached();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
      * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
      * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
@@ -590,7 +608,7 @@
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_STACK})
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public void notifyStatsProviderWarningOrLimitReached() {
+    public void notifyStatsProviderLimitReached() {
         try {
             mService.notifyStatsProviderWarningOrLimitReached();
         } catch (RemoteException e) {
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index c9dd06c..e3f02e7 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -59,6 +59,6 @@
     void getHibernationEligibility(
                 in String packageName,
                 in AndroidFuture callback);
-    void revokeOwnPermissionsOnKill(in String packageName, in List<String> permissions,
+    void revokeSelfPermissionsOnKill(in String packageName, in List<String> permissions,
             in AndroidFuture callback);
 }
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 619c870..6a93b35 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -76,8 +76,6 @@
 
     List<SplitPermissionInfoParcelable> getSplitPermissions();
 
-    void revokeOwnPermissionsOnKill(String packageName, in List<String> permissions);
-
     void startOneTimePermissionSession(String packageName, int userId, long timeout,
             long revokeAfterKilledDelay, int importanceToResetTimer,
             int importanceToKeepSessionAlive);
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index a005ab4e..3c2c7f0 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -916,15 +916,15 @@
      * @param packageName The name of the package for which the permissions will be revoked.
      * @param permissions List of permissions to be revoked.
      *
-     * @see Context#revokeOwnPermissionsOnKill(java.util.Collection)
+     * @see Context#revokeSelfPermissionsOnKill(java.util.Collection)
      *
      * @hide
      */
-    public void revokeOwnPermissionsOnKill(@NonNull String packageName,
+    public void revokeSelfPermissionsOnKill(@NonNull String packageName,
             @NonNull List<String> permissions) {
         mRemoteService.postAsync(service -> {
             AndroidFuture<Void> callback = new AndroidFuture<>();
-            service.revokeOwnPermissionsOnKill(packageName, permissions, callback);
+            service.revokeSelfPermissionsOnKill(packageName, permissions, callback);
             return callback;
         }).whenComplete((result, err) -> {
             if (err != null) {
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 3292e71..4efffc5a 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -40,6 +40,7 @@
 import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -339,10 +340,10 @@
      * @param permissions List of permissions to be revoked.
      * @param callback Callback waiting for operation to be complete.
      *
-     * @see PermissionManager#revokeOwnPermissionsOnKill(java.util.Collection)
+     * @see android.content.Context#revokeSelfPermissionsOnKill(java.util.Collection)
      */
     @BinderThread
-    public void onRevokeOwnPermissionsOnKill(@NonNull String packageName,
+    public void onRevokeSelfPermissionsOnKill(@NonNull String packageName,
             @NonNull List<String> permissions, @NonNull Runnable callback) {
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
@@ -703,13 +704,19 @@
             }
 
             @Override
-            public void revokeOwnPermissionsOnKill(@NonNull String packageName,
+            public void revokeSelfPermissionsOnKill(@NonNull String packageName,
                     @NonNull List<String> permissions, @NonNull AndroidFuture callback) {
                 try {
-                    enforceSomePermissionsGrantedToCaller(
-                            Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
                     Objects.requireNonNull(callback);
-                    onRevokeOwnPermissionsOnKill(packageName, permissions,
+
+                    final int callingUid = Binder.getCallingUid();
+                    int targetPackageUid = getPackageManager().getPackageUid(packageName,
+                            PackageManager.PackageInfoFlags.of(0));
+                    if (targetPackageUid != callingUid) {
+                        enforceSomePermissionsGrantedToCaller(
+                                Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
+                    }
+                    onRevokeSelfPermissionsOnKill(packageName, permissions,
                             () -> callback.complete(null));
                 } catch (Throwable t) {
                     callback.completeExceptionally(t);
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index c509de6..7a797ce 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -76,7 +76,6 @@
 import com.android.internal.util.CollectionUtils;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -626,19 +625,6 @@
     }
 
     /**
-     * @see Context#revokeOwnPermissionsOnKill(Collection)
-     * @hide
-     */
-    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
-        try {
-            mPermissionManager.revokeOwnPermissionsOnKill(mContext.getPackageName(),
-                    new ArrayList<String>(permissions));
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Gets the state flags associated with a permission.
      *
      * @param packageName the package name for which to get the flags
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionResult.java b/core/java/android/service/ambientcontext/AmbientContextDetectionResult.java
index 227194e..a216ed5 100644
--- a/core/java/android/service/ambientcontext/AmbientContextDetectionResult.java
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionResult.java
@@ -26,6 +26,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Represents a {@code AmbientContextEvent} detection result reported by the detection service.
@@ -127,7 +128,9 @@
         private @NonNull String mPackageName;
         private long mBuilderFieldsSet = 0L;
 
-        public Builder() {
+        public Builder(@NonNull String packageName) {
+            Objects.requireNonNull(packageName);
+            mPackageName = packageName;
         }
 
         /**
@@ -144,26 +147,37 @@
         }
 
         /**
-         * The package to deliver the response to.
+         * Adds a list of events to the builder.
          */
-        public @NonNull Builder setPackageName(@NonNull String value) {
+        public @NonNull Builder addEvents(@NonNull List<AmbientContextEvent> values) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x2;
-            mPackageName = value;
+            if (mEvents == null) {
+                mBuilderFieldsSet |= 0x1;
+                mEvents = new ArrayList<>();
+            }
+            mEvents.addAll(values);
+            return this;
+        }
+
+        /**
+         * Clears all events from the builder.
+         */
+        public @NonNull Builder clearEvents() {
+            checkNotUsed();
+            if (mEvents != null) {
+                mEvents.clear();
+            }
             return this;
         }
 
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull AmbientContextDetectionResult build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x4; // Mark builder used
+            mBuilderFieldsSet |= 0x2; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mEvents = new ArrayList<>();
             }
-            if ((mBuilderFieldsSet & 0x2) == 0) {
-                mPackageName = "";
-            }
             AmbientContextDetectionResult o = new AmbientContextDetectionResult(
                     mEvents,
                     mPackageName);
@@ -171,7 +185,7 @@
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x4) != 0) {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionService.java b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
index 6224aa1..8cf3411 100644
--- a/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
@@ -134,12 +134,18 @@
     }
 
     /**
-     * Starts detection and provides detected events to the statusConsumer. The ongoing detection
-     * will keep running, until onStopDetection is called. If there were previously requested
-     * detection from the same package, the previous request will be replaced with the new request.
-     * The implementation should keep track of whether the user consented each requested
-     * AmbientContextEvent for the app. If not consented, the statusConsumer should get a response
-     * with STATUS_ACCESS_DENIED.
+     * Called when a client app requests starting detection of the events in the request. The
+     * implementation should keep track of whether the user has explicitly consented to detecting
+     * the events using on-going ambient sensor (e.g. microphone), and agreed to share the
+     * detection results with this client app. If the user has not consented, the detection
+     * should not start, and the statusConsumer should get a response with STATUS_ACCESS_DENIED.
+     * If the user has made the consent and the underlying services are available, the
+     * implementation should start detection and provide detected events to the
+     * detectionResultConsumer. If the type of event needs immediate attention, the implementation
+     * should send result as soon as detected. Otherwise, the implementation can bulk send response.
+     * The ongoing detection will keep running, until onStopDetection is called. If there were
+     * previously requested detection from the same package, regardless of the type of events in
+     * the request, the previous request will be replaced with the new request.
      *
      * @param request The request with events to detect.
      * @param packageName the requesting app's package name
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.java b/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.java
index 3e92f39..199e674 100644
--- a/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.java
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.java
@@ -24,6 +24,8 @@
 
 import com.android.internal.util.AnnotationValidations;
 
+import java.util.Objects;
+
 /**
  * Represents a status for the {@code AmbientContextDetectionService}.
  *
@@ -121,7 +123,9 @@
         private @NonNull String mPackageName;
         private long mBuilderFieldsSet = 0L;
 
-        public Builder() {
+        public Builder(@NonNull String packageName) {
+            Objects.requireNonNull(packageName);
+            mPackageName = packageName;
         }
 
         /**
@@ -134,27 +138,14 @@
             return this;
         }
 
-        /**
-         * The package to deliver the response to.
-         */
-        public @NonNull Builder setPackageName(@NonNull String value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x2;
-            mPackageName = value;
-            return this;
-        }
-
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull AmbientContextDetectionServiceStatus build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x4; // Mark builder used
+            mBuilderFieldsSet |= 0x2; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mStatusCode = AmbientContextManager.STATUS_UNKNOWN;
             }
-            if ((mBuilderFieldsSet & 0x2) == 0) {
-                mPackageName = "";
-            }
             AmbientContextDetectionServiceStatus o = new AmbientContextDetectionServiceStatus(
                     mStatusCode,
                     mPackageName);
@@ -162,7 +153,7 @@
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x4) != 0) {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 34e7ea7..fe6ae78 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -96,7 +96,7 @@
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
         DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
         DEFAULT_FLAGS.put("settings_search_always_expand", "true");
-        DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "false");
+        DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "true");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
         DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "false");
         DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true");
diff --git a/core/java/android/util/TimingsTraceLog.java b/core/java/android/util/TimingsTraceLog.java
index 066709f..48a5cea 100644
--- a/core/java/android/util/TimingsTraceLog.java
+++ b/core/java/android/util/TimingsTraceLog.java
@@ -147,7 +147,7 @@
      * Logs a duration so it can be parsed by external tools for performance reporting.
      */
     public void logDuration(String name, long timeMs) {
-        Slog.d(mTag, name + " took to complete: " + timeMs + "ms");
+        Slog.v(mTag, name + " took to complete: " + timeMs + "ms");
     }
 
     /**
diff --git a/core/java/android/view/ContentRecordingSession.java b/core/java/android/view/ContentRecordingSession.java
index db4ec11..c66c70af 100644
--- a/core/java/android/view/ContentRecordingSession.java
+++ b/core/java/android/view/ContentRecordingSession.java
@@ -66,10 +66,11 @@
     private int mContentToRecord = RECORD_CONTENT_DISPLAY;
 
     /**
-     * The window token of the layer of the hierarchy to record.
-     * The display content if {@link #getContentToRecord()} is
-     * {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is
-     * {@link RecordContent#RECORD_CONTENT_TASK}.
+     * The token of the layer of the hierarchy to record.
+     * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then
+     * represents the WindowToken corresponding to the DisplayContent to record.
+     * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
+     * represents the {@link android.window.WindowContainerToken} of the Task to record.
      */
     @VisibleForTesting
     @Nullable
@@ -192,10 +193,11 @@
     }
 
     /**
-     * The window token of the layer of the hierarchy to record.
-     * The display content if {@link #getContentToRecord()} is
-     * {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is
-     * {@link RecordContent#RECORD_CONTENT_TASK}.
+     * {The token of the layer of the hierarchy to record.
+     * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then
+     * represents the WindowToken corresponding to the DisplayContent to record.
+     * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
+     * represents the {@link android.window.WindowContainerToken} of the Task to record.
      */
     @DataClass.Generated.Member
     public @VisibleForTesting @Nullable IBinder getTokenToRecord() {
@@ -231,10 +233,11 @@
     }
 
     /**
-     * The window token of the layer of the hierarchy to record.
-     * The display content if {@link #getContentToRecord()} is
-     * {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is
-     * {@link RecordContent#RECORD_CONTENT_TASK}.
+     * {The token of the layer of the hierarchy to record.
+     * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then
+     * represents the WindowToken corresponding to the DisplayContent to record.
+     * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
+     * represents the {@link android.window.WindowContainerToken} of the Task to record.
      */
     @DataClass.Generated.Member
     public @NonNull ContentRecordingSession setTokenToRecord(@VisibleForTesting @NonNull IBinder value) {
@@ -390,10 +393,11 @@
         }
 
         /**
-         * The window token of the layer of the hierarchy to record.
-         * The display content if {@link #getContentToRecord()} is
-         * {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is
-         * {@link RecordContent#RECORD_CONTENT_TASK}.
+         * {The token of the layer of the hierarchy to record.
+         * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then
+         * represents the WindowToken corresponding to the DisplayContent to record.
+         * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
+         * represents the {@link android.window.WindowContainerToken} of the Task to record.
          */
         @DataClass.Generated.Member
         public @NonNull Builder setTokenToRecord(@VisibleForTesting @NonNull IBinder value) {
@@ -433,7 +437,7 @@
     }
 
     @DataClass.Generated(
-            time = 1644843382972L,
+            time = 1645803878639L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/ContentRecordingSession.java",
             inputSignatures = "public static final  int RECORD_CONTENT_DISPLAY\npublic static final  int RECORD_CONTENT_TASK\nprivate  int mDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate @com.android.internal.annotations.VisibleForTesting @android.annotation.Nullable android.os.IBinder mTokenToRecord\npublic static  android.view.ContentRecordingSession createDisplaySession(android.os.IBinder)\npublic static  android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static  boolean isValid(android.view.ContentRecordingSession)\npublic static  boolean isSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 190adbd..61098d6 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -55,10 +55,10 @@
      */
     private final int mTouchSlop;
     /**
-     * The timeout used to distinguish tap from handwriting. If the stylus doesn't move before this
-     * timeout, it's not considered as handwriting.
+     * The timeout used to distinguish tap or long click from handwriting. If the stylus doesn't
+     * move before this timeout, it's not considered as handwriting.
      */
-    private final long mTapTimeoutInMillis;
+    private final long mHandwritingTimeoutInMillis;
 
     private State mState = new State();
     private final HandwritingAreaTracker mHandwritingAreasTracker = new HandwritingAreaTracker();
@@ -90,7 +90,7 @@
     public HandwritingInitiator(@NonNull ViewConfiguration viewConfiguration,
             @NonNull InputMethodManager inputMethodManager) {
         mTouchSlop = viewConfiguration.getScaledTouchSlop();
-        mTapTimeoutInMillis = ViewConfiguration.getTapTimeout();
+        mHandwritingTimeoutInMillis = ViewConfiguration.getLongPressTimeout();
         mImm = inputMethodManager;
     }
 
@@ -145,7 +145,7 @@
 
                 final long timeElapsed =
                         motionEvent.getEventTime() - mState.mStylusDownTimeInMillis;
-                if (timeElapsed > mTapTimeoutInMillis) {
+                if (timeElapsed > mHandwritingTimeoutInMillis) {
                     reset();
                     return;
                 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 64f5668..f3c65ea 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -64,6 +64,7 @@
 import android.view.Surface.OutOfResourcesException;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.VirtualRefBasePtr;
 
 import dalvik.system.CloseGuard;
@@ -2961,6 +2962,8 @@
         @NonNull
         public Transaction setScale(@NonNull SurfaceControl sc, float scaleX, float scaleY) {
             checkPreconditions(sc);
+            Preconditions.checkArgument(scaleX >= 0, "Negative value passed in for scaleX");
+            Preconditions.checkArgument(scaleY >= 0, "Negative value passed in for scaleY");
             nativeSetScale(mNativeObject, sc.mNativeObject, scaleX, scaleY);
             return this;
         }
@@ -3205,6 +3208,7 @@
         public @NonNull Transaction setCrop(@NonNull SurfaceControl sc, @Nullable Rect crop) {
             checkPreconditions(sc);
             if (crop != null) {
+                Preconditions.checkArgument(crop.isValid(), "Crop isn't valid.");
                 nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
                         crop.left, crop.top, crop.right, crop.bottom);
             } else {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cdbf8b4..b72725a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3165,11 +3165,8 @@
             // possible that checking the most recent value is actually more
             // correct here.
             if (!mStopped || wasReportNextDraw) {
-                boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
-                        (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
-                if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
-                        || mHeight != host.getMeasuredHeight() || dispatchApplyInsets ||
-                        updatedConfiguration) {
+                if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()
+                        || dispatchApplyInsets || updatedConfiguration) {
                     int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width,
                             lp.privateFlags);
                     int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height,
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 2dc5fbd..b0da877 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -60,47 +60,41 @@
     private static boolean sUseBLASTAdapter = false;
 
     /**
-     * The user is navigating with keys (not the touch screen), so
-     * navigational focus should be shown.
-     */
-    public static final int RELAYOUT_RES_IN_TOUCH_MODE = 0x1;
-
-    /**
      * This is the first time the window is being drawn,
      * so the client must call drawingFinished() when done
      */
-    public static final int RELAYOUT_RES_FIRST_TIME = 0x2;
+    public static final int RELAYOUT_RES_FIRST_TIME = 1;
 
     /**
      * The window manager has changed the surface from the last call.
      */
-    public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4;
+    public static final int RELAYOUT_RES_SURFACE_CHANGED = 1 << 1;
 
     /**
      * The window is being resized by dragging on the docked divider. The client should render
      * at (0, 0) and extend its background to the background frame passed into
      * {@link IWindow#resized}.
      */
-    public static final int RELAYOUT_RES_DRAG_RESIZING_DOCKED = 0x8;
+    public static final int RELAYOUT_RES_DRAG_RESIZING_DOCKED = 1 << 2;
 
     /**
      * The window is being resized by dragging one of the window corners,
      * in this case the surface would be fullscreen-sized. The client should
      * render to the actual frame location (instead of (0,curScrollY)).
      */
-    public static final int RELAYOUT_RES_DRAG_RESIZING_FREEFORM = 0x10;
+    public static final int RELAYOUT_RES_DRAG_RESIZING_FREEFORM = 1 << 3;
 
     /**
      * The window manager has changed the size of the surface from the last call.
      */
-    public static final int RELAYOUT_RES_SURFACE_RESIZED = 0x20;
+    public static final int RELAYOUT_RES_SURFACE_RESIZED = 1 << 4;
 
     /**
      * In multi-window we force show the system bars. Because we don't want that the surface size
      * changes in this mode, we instead have a flag whether the system bar sizes should always be
      * consumed, so the app is treated like there is no virtual system bars at all.
      */
-    public static final int RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS = 0x40;
+    public static final int RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS = 1 << 5;
 
     /**
      * This flag indicates the client should not directly submit it's next frame,
@@ -108,7 +102,7 @@
      * {@link WindowManagerService#finishDrawing}. This is used by the WM
      * BLASTSyncEngine to synchronize rendering of multiple windows.
      */
-    public static final int RELAYOUT_RES_BLAST_SYNC = 0x80;
+    public static final int RELAYOUT_RES_BLAST_SYNC = 1 << 6;
 
     /**
      * Flag for relayout: the client will be later giving
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index ad151df..06588b2 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -332,8 +332,7 @@
             outInsetsState.set(mInsetsState);
         }
 
-        // Include whether the window is in touch mode.
-        return isInTouchMode() ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
+        return 0;
     }
 
     @Override
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 34c47ed..06bc4b5 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -33,6 +33,7 @@
 import android.graphics.HardwareRendererObserver;
 import android.os.Handler;
 import android.os.Trace;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Choreographer;
@@ -188,6 +189,7 @@
                             if (mBeginVsyncId != INVALID_ID) {
                                 mSurfaceControlWrapper.addJankStatsListener(
                                         FrameTracker.this, mSurfaceControl);
+                                markEvent("FT#deferMonitoring");
                                 postTraceStartMarker();
                             }
                         }
@@ -241,8 +243,9 @@
             }
             if (mSurfaceControl != null) {
                 if (mDeferMonitoring) {
+                    markEvent("FT#deferMonitoring");
                     // Normal case, we begin the instrument from the very beginning,
-                    // except the first frame.
+                    // will exclude the first frame.
                     postTraceStartMarker();
                 } else {
                     // If we don't begin the instrument from the very beginning,
@@ -272,6 +275,7 @@
                 return;
             }
             mTracingStarted = true;
+            markEvent("FT#begin");
             Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
         }
     }
@@ -295,6 +299,7 @@
                     Log.d(TAG, "end: " + mSession.getName()
                             + ", end=" + mEndVsyncId + ", reason=" + reason);
                 }
+                markEvent("FT#end#" + reason);
                 Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
                 mSession.setReason(reason);
 
@@ -322,6 +327,7 @@
                     reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC;
             if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false;
             mCancelled = true;
+            markEvent("FT#cancel#" + reason);
             // We don't need to end the trace section if it never begun.
             if (mTracingStarted) {
                 Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
@@ -343,6 +349,11 @@
         }
     }
 
+    private void markEvent(String desc) {
+        Trace.beginSection(TextUtils.formatSimple("%s#%s", mSession.getName(), desc));
+        Trace.endSection();
+    }
+
     private void notifyCujEvent(String action) {
         if (mListener == null) return;
         mListener.onCujEvents(mSession, action);
diff --git a/core/java/com/android/internal/os/KernelAllocationStats.java b/core/java/com/android/internal/os/KernelAllocationStats.java
index 1c3f8b0..58d51e3 100644
--- a/core/java/com/android/internal/os/KernelAllocationStats.java
+++ b/core/java/com/android/internal/os/KernelAllocationStats.java
@@ -24,21 +24,29 @@
 
     /** Process dma-buf stats. */
     public static final class ProcessDmabuf {
+        public final int uid;
+        public final String processName;
+        public final int oomScore;
+
         /** Size of buffers retained by the process. */
         public final int retainedSizeKb;
         /** Number of buffers retained by the process. */
         public final int retainedBuffersCount;
-        /** Size of buffers mapped to the address space. */
-        public final int mappedSizeKb;
-        /** Count of buffers mapped to the address space. */
-        public final int mappedBuffersCount;
+        /** Size of buffers shared with Surface Flinger. */
+        public final int surfaceFlingerSizeKb;
+        /** Count of buffers shared with Surface Flinger. */
+        public final int surfaceFlingerCount;
 
-        ProcessDmabuf(int retainedSizeKb, int retainedBuffersCount,
-                int mappedSizeKb, int mappedBuffersCount) {
+        ProcessDmabuf(int uid, String processName, int oomScore, int retainedSizeKb,
+                int retainedBuffersCount, int surfaceFlingerSizeKb,
+                int surfaceFlingerCount) {
+            this.uid = uid;
+            this.processName = processName;
+            this.oomScore = oomScore;
             this.retainedSizeKb = retainedSizeKb;
             this.retainedBuffersCount = retainedBuffersCount;
-            this.mappedSizeKb = mappedSizeKb;
-            this.mappedBuffersCount = mappedBuffersCount;
+            this.surfaceFlingerSizeKb = surfaceFlingerSizeKb;
+            this.surfaceFlingerCount = surfaceFlingerCount;
         }
     }
 
@@ -47,7 +55,7 @@
      * stats could not be read.
      */
     @Nullable
-    public static native ProcessDmabuf getDmabufAllocations(int pid);
+    public static native ProcessDmabuf[] getDmabufAllocations();
 
     /** Pid to gpu memory size. */
     public static final class ProcessGpuMem {
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index bd27e60..299cbe1 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -23,7 +23,6 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
@@ -36,9 +35,6 @@
 import android.widget.ImageView;
 import android.widget.RemoteViews;
 
-import com.android.internal.R;
-
-import java.io.IOException;
 import java.util.Objects;
 import java.util.function.Consumer;
 
@@ -59,42 +55,9 @@
     private int mBackgroundColor;
     private boolean mWillBeForceHidden;
 
-    private int mMaxDrawableWidth = -1;
-    private int mMaxDrawableHeight = -1;
-
-    public CachingIconView(Context context) {
-        this(context, null, 0, 0);
-    }
-
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public CachingIconView(Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0, 0);
-    }
-
-    public CachingIconView(Context context, @Nullable AttributeSet attrs,
-            int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public CachingIconView(Context context, @Nullable AttributeSet attrs,
-            int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        init(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        if (attrs == null) {
-            return;
-        }
-
-        TypedArray ta = context.obtainStyledAttributes(attrs,
-                R.styleable.CachingIconView, defStyleAttr, defStyleRes);
-        mMaxDrawableWidth = ta.getDimensionPixelSize(R.styleable
-                .CachingIconView_maxDrawableWidth, -1);
-        mMaxDrawableHeight = ta.getDimensionPixelSize(R.styleable
-                .CachingIconView_maxDrawableHeight, -1);
-        ta.recycle();
+        super(context, attrs);
     }
 
     @Override
@@ -103,31 +66,15 @@
         if (!testAndSetCache(icon)) {
             mInternalSetDrawable = true;
             // This calls back to setImageDrawable, make sure we don't clear the cache there.
-            Drawable drawable = loadSizeRestrictedIcon(icon);
-            if (drawable == null) {
-                super.setImageIcon(icon);
-            } else {
-                super.setImageDrawable(drawable);
-            }
+            super.setImageIcon(icon);
             mInternalSetDrawable = false;
         }
     }
 
-    @Nullable
-    private Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
-        try {
-            return LocalImageResolver.resolveImage(icon, getContext(), mMaxDrawableWidth,
-                    mMaxDrawableHeight);
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
     @Override
-    public Runnable setImageIconAsync(@Nullable final Icon icon) {
+    public Runnable setImageIconAsync(@Nullable Icon icon) {
         resetCache();
-        Drawable drawable = loadSizeRestrictedIcon(icon);
-        return () -> setImageDrawable(drawable);
+        return super.setImageIconAsync(icon);
     }
 
     @Override
@@ -136,34 +83,14 @@
         if (!testAndSetCache(resId)) {
             mInternalSetDrawable = true;
             // This calls back to setImageDrawable, make sure we don't clear the cache there.
-            Drawable drawable = loadSizeRestrictedDrawable(resId);
-            if (drawable == null) {
-                super.setImageResource(resId);
-            } else {
-                super.setImageDrawable(drawable);
-            }
+            super.setImageResource(resId);
             mInternalSetDrawable = false;
         }
     }
 
-    @Nullable
-    private Drawable loadSizeRestrictedDrawable(@DrawableRes int resId) {
-        try {
-            return LocalImageResolver.resolveImage(resId, getContext(), mMaxDrawableWidth,
-                    mMaxDrawableHeight);
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
     @Override
     public Runnable setImageResourceAsync(@DrawableRes int resId) {
         resetCache();
-        Drawable drawable = loadSizeRestrictedDrawable(resId);
-        if (drawable != null) {
-            return () -> setImageDrawable(drawable);
-        }
-
         return super.setImageResourceAsync(resId);
     }
 
@@ -171,35 +98,13 @@
     @RemotableViewMethod(asyncImpl="setImageURIAsync")
     public void setImageURI(@Nullable Uri uri) {
         resetCache();
-        Drawable drawable = loadSizeRestrictedUri(uri);
-        if (drawable == null) {
-            super.setImageURI(uri);
-        } else {
-            mInternalSetDrawable = true;
-            super.setImageDrawable(drawable);
-            mInternalSetDrawable = false;
-        }
-    }
-
-    @Nullable
-    private Drawable loadSizeRestrictedUri(@Nullable Uri uri) {
-        try {
-            return LocalImageResolver.resolveImage(uri, getContext(), mMaxDrawableWidth,
-                    mMaxDrawableHeight);
-        } catch (IOException e) {
-            return null;
-        }
+        super.setImageURI(uri);
     }
 
     @Override
     public Runnable setImageURIAsync(@Nullable Uri uri) {
         resetCache();
-        Drawable drawable = loadSizeRestrictedUri(uri);
-        if (drawable == null) {
-            return super.setImageURIAsync(uri);
-        } else {
-            return () -> setImageDrawable(drawable);
-        }
+        return super.setImageURIAsync(uri);
     }
 
     @Override
@@ -402,18 +307,4 @@
     public void setWillBeForceHidden(boolean forceHidden) {
         mWillBeForceHidden = forceHidden;
     }
-
-    /**
-     * Returns the set maximum width of drawable in pixels. -1 if not set.
-     */
-    public int getMaxDrawableWidth() {
-        return mMaxDrawableWidth;
-    }
-
-    /**
-     * Returns the set maximum height of drawable in pixels. -1 if not set.
-     */
-    public int getMaxDrawableHeight() {
-        return mMaxDrawableHeight;
-    }
 }
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java
index 66a3ff9..616b699 100644
--- a/core/java/com/android/internal/widget/LocalImageResolver.java
+++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -16,25 +16,21 @@
 
 package com.android.internal.widget;
 
-import android.annotation.DrawableRes;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.graphics.ImageDecoder;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.util.Size;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.IOException;
 
 /** A class to extract Drawables from a MessagingStyle/ConversationStyle message. */
 public class LocalImageResolver {
+    private static final String TAG = LocalImageResolver.class.getSimpleName();
 
-    @VisibleForTesting
-    static final int DEFAULT_MAX_SAFE_ICON_SIZE_PX = 480;
+    private static final int MAX_SAFE_ICON_SIZE_PX = 480;
 
     /**
      * Resolve an image from the given Uri using {@link ImageDecoder}
@@ -42,9 +38,9 @@
     public static Drawable resolveImage(Uri uri, Context context) throws IOException {
         final ImageDecoder.Source source =
                 ImageDecoder.createSource(context.getContentResolver(), uri);
-        return ImageDecoder.decodeDrawable(source,
-                (decoder, info, s) -> LocalImageResolver.onHeaderDecoded(decoder, info,
-                        DEFAULT_MAX_SAFE_ICON_SIZE_PX, DEFAULT_MAX_SAFE_ICON_SIZE_PX));
+        final Drawable drawable =
+                ImageDecoder.decodeDrawable(source, LocalImageResolver::onHeaderDecoded);
+        return drawable;
     }
 
     /**
@@ -52,49 +48,17 @@
      * using {@link Icon#loadDrawable(Context)} otherwise.  This will correctly apply the Icon's,
      * tint, if present, to the drawable.
      */
-    public static Drawable resolveImage(@Nullable Icon icon, Context context)
-            throws IOException {
-        return resolveImage(icon, context, DEFAULT_MAX_SAFE_ICON_SIZE_PX,
-                DEFAULT_MAX_SAFE_ICON_SIZE_PX);
-    }
-
-    /**
-     * Get the drawable from Icon using {@link ImageDecoder} if it contains a Uri, or
-     * using {@link Icon#loadDrawable(Context)} otherwise.  This will correctly apply the Icon's,
-     * tint, if present, to the drawable.
-     */
-    @Nullable
-    public static Drawable resolveImage(@Nullable Icon icon, Context context, int maxWidth,
-            int maxHeight)
-            throws IOException {
-        if (icon == null) {
-            return null;
+    public static Drawable resolveImage(Icon icon, Context context) throws IOException {
+        Uri uri = getResolvableUri(icon);
+        if (uri != null) {
+            Drawable result = resolveImage(uri, context);
+            if (icon.hasTint()) {
+                result.mutate();
+                result.setTintList(icon.getTintList());
+                result.setTintBlendMode(icon.getTintBlendMode());
+            }
+            return result;
         }
-
-        switch (icon.getType()) {
-            case Icon.TYPE_URI:
-            case Icon.TYPE_URI_ADAPTIVE_BITMAP:
-                Uri uri = getResolvableUri(icon);
-                if (uri != null) {
-                    Drawable result = resolveImage(uri, context, maxWidth, maxHeight);
-                    return tintDrawable(icon, result);
-                }
-                break;
-            case Icon.TYPE_RESOURCE:
-                Drawable result = resolveImage(icon.getResId(), context, maxWidth, maxHeight);
-                if (result != null) {
-                    return tintDrawable(icon, result);
-                }
-                break;
-            case Icon.TYPE_BITMAP:
-            case Icon.TYPE_ADAPTIVE_BITMAP:
-                return resolveBitmapImage(icon, context, maxWidth, maxHeight);
-            case Icon.TYPE_DATA:    // We can't really improve on raw data images.
-            default:
-                break;
-        }
-
-        // Fallback to straight drawable load if we fail with more efficient approach.
         return icon.loadDrawable(context);
     }
 
@@ -102,71 +66,7 @@
             throws IOException {
         final ImageDecoder.Source source =
                 ImageDecoder.createSource(context.getContentResolver(), uri);
-        return resolveImage(source, maxWidth, maxHeight);
-    }
-
-    /**
-     * Attempts to resolve the resource as a bitmap drawable constrained within max sizes.
-     *
-     * @return decoded drawable or null if the passed resource is not a straight bitmap
-     */
-    @Nullable
-    public static Drawable resolveImage(@DrawableRes int resId, Context context, int maxWidth,
-            int maxHeight)
-            throws IOException {
-        final ImageDecoder.Source source = ImageDecoder.createSource(context.getResources(), resId);
-        // It's possible that the resource isn't an actual bitmap drawable so this decode can fail.
-        // Return null in that case.
-        try {
-            return resolveImage(source, maxWidth, maxHeight);
-        } catch (ImageDecoder.DecodeException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    private static Drawable resolveBitmapImage(Icon icon, Context context, int maxWidth,
-            int maxHeight) {
-        Bitmap bitmap = icon.getBitmap();
-        if (bitmap == null) {
-            return null;
-        }
-
-        if (bitmap.getWidth() > maxWidth || bitmap.getHeight() > maxHeight) {
-            Icon smallerIcon = icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP
-                    ? Icon.createWithAdaptiveBitmap(bitmap) : Icon.createWithBitmap(bitmap);
-            // We don't want to modify the source icon, create a copy.
-            smallerIcon.setTintList(icon.getTintList())
-                    .setTintBlendMode(icon.getTintBlendMode())
-                    .scaleDownIfNecessary(maxWidth, maxHeight);
-            return smallerIcon.loadDrawable(context);
-        }
-
-        return icon.loadDrawable(context);
-    }
-
-    @Nullable
-    private static Drawable tintDrawable(Icon icon, @Nullable Drawable drawable) {
-        if (drawable == null) {
-            return null;
-        }
-
-        if (icon.hasTint()) {
-            drawable.mutate();
-            drawable.setTintList(icon.getTintList());
-            drawable.setTintBlendMode(icon.getTintBlendMode());
-        }
-
-        return drawable;
-    }
-
-    private static Drawable resolveImage(ImageDecoder.Source source, int maxWidth, int maxHeight)
-            throws IOException {
         return ImageDecoder.decodeDrawable(source, (decoder, info, unused) -> {
-            if (maxWidth <= 0 || maxHeight <= 0) {
-                return;
-            }
-
             final Size size = info.getSize();
             if (size.getWidth() > size.getHeight()) {
                 if (size.getWidth() > maxWidth) {
@@ -188,12 +88,11 @@
     }
 
     private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info,
-            int maxWidth, int maxHeight) {
+            ImageDecoder.Source source) {
         final Size size = info.getSize();
         final int originalSize = Math.max(size.getHeight(), size.getWidth());
-        final int maxSize = Math.max(maxWidth, maxHeight);
-        final double ratio = (originalSize > maxSize)
-                ? originalSize * 1f / maxSize
+        final double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX)
+                ? originalSize * 1f / MAX_SAFE_ICON_SIZE_PX
                 : 1.0;
         decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio));
     }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index cc7e9d9..e281853 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -34,6 +34,7 @@
 import android.os.incremental.IncrementalManager;
 import android.os.storage.StorageManager;
 import android.permission.PermissionManager.SplitPermissionInfo;
+import android.sysprop.ApexProperties;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -1187,7 +1188,8 @@
                             boolean systemExt = permFile.toPath().startsWith(
                                     Environment.getSystemExtDirectory().toPath() + "/");
                             boolean apex = permFile.toPath().startsWith(
-                                    Environment.getApexDirectory().toPath() + "/");
+                                    Environment.getApexDirectory().toPath() + "/")
+                                    && ApexProperties.updatable().orElse(false);
                             if (vendor) {
                                 readPrivAppPermissions(parser, mVendorPrivAppPermissions,
                                         mVendorPrivAppDenyPermissions);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 3a76745..a1be884 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -272,6 +272,7 @@
                 "libinput",
                 "libcamera_client",
                 "libcamera_metadata",
+                "libprocinfo",
                 "libsqlite",
                 "libEGL",
                 "libGLESv1_CM",
diff --git a/core/jni/com_android_internal_os_KernelAllocationStats.cpp b/core/jni/com_android_internal_os_KernelAllocationStats.cpp
index e0a24430..5b10497 100644
--- a/core/jni/com_android_internal_os_KernelAllocationStats.cpp
+++ b/core/jni/com_android_internal_os_KernelAllocationStats.cpp
@@ -13,12 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <dmabufinfo/dmabufinfo.h>
 #include <jni.h>
 #include <meminfo/sysmeminfo.h>
+#include <procinfo/process.h>
 
 #include "core_jni_helpers.h"
 
+using DmaBuffer = ::android::dmabufinfo::DmaBuffer;
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
+
 namespace {
 static jclass gProcessDmabufClazz;
 static jmethodID gProcessDmabufCtor;
@@ -28,30 +35,127 @@
 
 namespace android {
 
-static jobject KernelAllocationStats_getDmabufAllocations(JNIEnv *env, jobject, jint pid) {
-    std::vector<dmabufinfo::DmaBuffer> buffers;
-    if (!dmabufinfo::ReadDmaBufMapRefs(pid, &buffers)) {
+struct PidDmaInfo {
+    uid_t uid;
+    std::string cmdline;
+    int oomScoreAdj;
+};
+
+static jobjectArray KernelAllocationStats_getDmabufAllocations(JNIEnv *env, jobject) {
+    std::vector<DmaBuffer> buffers;
+
+    if (!dmabufinfo::ReadDmaBufs(&buffers)) {
         return nullptr;
     }
-    jint mappedSize = 0;
-    jint mappedCount = buffers.size();
-    for (const auto &buffer : buffers) {
-        mappedSize += buffer.size();
-    }
-    mappedSize /= 1024;
 
-    jint retainedSize = -1;
-    jint retainedCount = -1;
-    if (dmabufinfo::ReadDmaBufFdRefs(pid, &buffers)) {
-        retainedCount = buffers.size();
-        retainedSize = 0;
-        for (const auto &buffer : buffers) {
-            retainedSize += buffer.size();
+    // Create a reverse map from pid to dmabufs
+    // Store dmabuf inodes & sizes for later processing.
+    std::unordered_map<pid_t, std::set<ino_t>> pidToInodes;
+    std::unordered_map<ino_t, long> inodeToSize;
+    for (auto &buf : buffers) {
+        for (auto pid : buf.pids()) {
+            pidToInodes[pid].insert(buf.inode());
         }
-        retainedSize /= 1024;
+        inodeToSize[buf.inode()] = buf.size();
     }
-    return env->NewObject(gProcessDmabufClazz, gProcessDmabufCtor, retainedSize, retainedCount,
-                          mappedSize, mappedCount);
+
+    pid_t surfaceFlingerPid = -1;
+    // The set of all inodes that are being retained by SurfaceFlinger. Buffers
+    // shared between another process and SF will appear in this set.
+    std::set<ino_t> surfaceFlingerBufferInodes;
+    // The set of all inodes that are being retained by any process other
+    // than SurfaceFlinger. Buffers shared between another process and SF will
+    // appear in this set.
+    std::set<ino_t> otherProcessBufferInodes;
+
+    // Find SurfaceFlinger pid & get cmdlines, oomScoreAdj, etc for each pid
+    // holding any DMA buffers.
+    std::unordered_map<pid_t, PidDmaInfo> pidDmaInfos;
+    for (const auto &pidToInodeEntry : pidToInodes) {
+        pid_t pid = pidToInodeEntry.first;
+
+        android::procinfo::ProcessInfo processInfo;
+        if (!android::procinfo::GetProcessInfo(pid, &processInfo)) {
+            continue;
+        }
+
+        std::string cmdline;
+        if (!ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline)) {
+            continue;
+        }
+
+        // cmdline strings are null-delimited, so we split on \0 here
+        if (cmdline.substr(0, cmdline.find('\0')) == "/system/bin/surfaceflinger") {
+            if (surfaceFlingerPid == -1) {
+                surfaceFlingerPid = pid;
+                surfaceFlingerBufferInodes = pidToInodes[pid];
+            } else {
+                LOG(ERROR) << "getDmabufAllocations found multiple SF processes; pid1: " << pid
+                           << ", pid2:" << surfaceFlingerPid;
+                surfaceFlingerPid = -2; // Used as a sentinel value below
+            }
+        } else {
+            otherProcessBufferInodes.insert(pidToInodes[pid].begin(), pidToInodes[pid].end());
+        }
+
+        std::string oomScoreAdjStr;
+        if (!ReadFileToString(StringPrintf("/proc/%d/oom_score_adj", pid), &oomScoreAdjStr)) {
+            continue;
+        }
+
+        pidDmaInfos[pid] = PidDmaInfo{.uid = processInfo.uid,
+                                      .cmdline = cmdline,
+                                      .oomScoreAdj = atoi(oomScoreAdjStr.c_str())};
+    }
+
+    if (surfaceFlingerPid < 0) {
+        LOG(ERROR) << "getDmabufAllocations could not identify SurfaceFlinger "
+                   << "process via /proc/pid/cmdline";
+    }
+
+    jobjectArray ret = env->NewObjectArray(pidDmaInfos.size(), gProcessDmabufClazz, NULL);
+    int retArrayIndex = 0;
+    for (const auto &pidDmaInfosEntry : pidDmaInfos) {
+        pid_t pid = pidDmaInfosEntry.first;
+
+        // For all processes apart from SurfaceFlinger, this set will store the
+        // dmabuf inodes that are shared with SF. For SF, it will store the inodes
+        // that are shared with any other process.
+        std::set<ino_t> sharedBuffers;
+        if (pid == surfaceFlingerPid) {
+            set_intersection(surfaceFlingerBufferInodes.begin(), surfaceFlingerBufferInodes.end(),
+                             otherProcessBufferInodes.begin(), otherProcessBufferInodes.end(),
+                             std::inserter(sharedBuffers, sharedBuffers.end()));
+        } else if (surfaceFlingerPid > 0) {
+            set_intersection(pidToInodes[pid].begin(), pidToInodes[pid].end(),
+                             surfaceFlingerBufferInodes.begin(), surfaceFlingerBufferInodes.end(),
+                             std::inserter(sharedBuffers, sharedBuffers.begin()));
+        } // If surfaceFlingerPid < 0; it means we failed to identify it, and
+        // the SF-related fields below should be left empty.
+
+        long totalSize = 0;
+        long sharedBuffersSize = 0;
+        for (const auto &inode : pidToInodes[pid]) {
+            totalSize += inodeToSize[inode];
+            if (sharedBuffers.count(inode)) {
+                sharedBuffersSize += inodeToSize[inode];
+            }
+        }
+
+        jobject obj = env->NewObject(gProcessDmabufClazz, gProcessDmabufCtor,
+                                     /* uid */ pidDmaInfos[pid].uid,
+                                     /* process name */
+                                     env->NewStringUTF(pidDmaInfos[pid].cmdline.c_str()),
+                                     /* oomscore */ pidDmaInfos[pid].oomScoreAdj,
+                                     /* retainedSize */ totalSize / 1024,
+                                     /* retainedCount */ pidToInodes[pid].size(),
+                                     /* sharedWithSurfaceFlinger size */ sharedBuffersSize / 1024,
+                                     /* sharedWithSurfaceFlinger count */ sharedBuffers.size());
+
+        env->SetObjectArrayElement(ret, retArrayIndex++, obj);
+    }
+
+    return ret;
 }
 
 static jobject KernelAllocationStats_getGpuAllocations(JNIEnv *env) {
@@ -74,7 +178,7 @@
 }
 
 static const JNINativeMethod methods[] = {
-        {"getDmabufAllocations", "(I)Lcom/android/internal/os/KernelAllocationStats$ProcessDmabuf;",
+        {"getDmabufAllocations", "()[Lcom/android/internal/os/KernelAllocationStats$ProcessDmabuf;",
          (void *)KernelAllocationStats_getDmabufAllocations},
         {"getGpuAllocations", "()[Lcom/android/internal/os/KernelAllocationStats$ProcessGpuMem;",
          (void *)KernelAllocationStats_getGpuAllocations},
@@ -86,7 +190,8 @@
     jclass clazz =
             FindClassOrDie(env, "com/android/internal/os/KernelAllocationStats$ProcessDmabuf");
     gProcessDmabufClazz = MakeGlobalRefOrDie(env, clazz);
-    gProcessDmabufCtor = GetMethodIDOrDie(env, gProcessDmabufClazz, "<init>", "(IIII)V");
+    gProcessDmabufCtor =
+            GetMethodIDOrDie(env, gProcessDmabufClazz, "<init>", "(ILjava/lang/String;IIIII)V");
 
     clazz = FindClassOrDie(env, "com/android/internal/os/KernelAllocationStats$ProcessGpuMem");
     gProcessGpuMemClazz = MakeGlobalRefOrDie(env, clazz);
@@ -94,4 +199,4 @@
     return res;
 }
 
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/core/proto/android/os/appbatterystats.proto b/core/proto/android/os/appbatterystats.proto
new file mode 100644
index 0000000..8769ebb
--- /dev/null
+++ b/core/proto/android/os/appbatterystats.proto
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.os;
+
+option java_multiple_files = true;
+
+message AppBatteryStatsProto {
+  message UidStats {
+    optional int32 uid = 1;
+
+    message ProcessStateStats {
+      enum ProcessState {
+        UNSPECIFIED = 0;
+        FOREGROUND = 1;
+        BACKGROUND = 2;
+        FOREGROUND_SERVICE = 3;
+        CACHED = 4;
+      }
+
+      optional ProcessState process_state = 1;
+
+      // Time spent in this state in the past 24 hours
+      optional int64 duration_ms = 2;
+      // Estimated power consumed in this state in the past 24 hours
+      optional double power_mah = 3;
+    }
+
+    repeated ProcessStateStats process_state_stats = 2;
+  }
+
+  repeated UidStats uid_stats = 1;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 729bc82..d652b2f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6051,10 +6051,10 @@
     <permission android:name="android.permission.MANAGE_APPOPS"
                 android:protectionLevel="signature" />
 
-    <!-- @SystemApi Permission that allows background clipboard access.
-         @hide Not for use by third-party applications. -->
+    <!-- @hide Permission that allows background clipboard access.
+         <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND"
-        android:protectionLevel="internal|role" />
+        android:protectionLevel="signature" />
     <!-- @hide Permission that suppresses the notification when the clipboard is accessed.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION"
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index a7f2aa7..81a79c5 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -49,8 +49,6 @@
         android:layout_marginStart="@dimen/notification_icon_circle_start"
         android:background="@drawable/notification_icon_circle"
         android:padding="@dimen/notification_icon_circle_padding"
-        android:maxDrawableWidth="@dimen/notification_icon_circle_size"
-        android:maxDrawableHeight="@dimen/notification_icon_circle_size"
         />
 
     <!-- extends ViewGroup -->
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index fd787f6..c6983ae 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -45,8 +45,6 @@
         android:layout_marginStart="@dimen/notification_icon_circle_start"
         android:background="@drawable/notification_icon_circle"
         android:padding="@dimen/notification_icon_circle_padding"
-        android:maxDrawableWidth="@dimen/notification_icon_circle_size"
-        android:maxDrawableHeight="@dimen/notification_icon_circle_size"
         />
 
     <FrameLayout
@@ -138,7 +136,7 @@
 
         </LinearLayout>
 
-        <com.android.internal.widget.CachingIconView
+        <ImageView
             android:id="@+id/right_icon"
             android:layout_width="@dimen/notification_right_icon_size"
             android:layout_height="@dimen/notification_right_icon_size"
@@ -150,8 +148,6 @@
             android:clipToOutline="true"
             android:importantForAccessibility="no"
             android:scaleType="centerCrop"
-            android:maxDrawableWidth="@dimen/notification_right_icon_size"
-            android:maxDrawableHeight="@dimen/notification_right_icon_size"
             />
 
         <FrameLayout
diff --git a/core/res/res/layout/notification_template_right_icon.xml b/core/res/res/layout/notification_template_right_icon.xml
index 8b3b795..f163ed5 100644
--- a/core/res/res/layout/notification_template_right_icon.xml
+++ b/core/res/res/layout/notification_template_right_icon.xml
@@ -13,7 +13,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<com.android.internal.widget.CachingIconView
+<ImageView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/right_icon"
     android:layout_width="@dimen/notification_right_icon_size"
@@ -25,6 +25,4 @@
     android:clipToOutline="true"
     android:importantForAccessibility="no"
     android:scaleType="centerCrop"
-    android:maxDrawableWidth="@dimen/notification_right_icon_size"
-    android:maxDrawableHeight="@dimen/notification_right_icon_size"
     />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b3203ae..2107f65 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9807,12 +9807,4 @@
         of the supported locale. {@link android.app.LocaleConfig} -->
         <attr name="name" />
     </declare-styleable>
-
-    <!-- @hide -->
-    <declare-styleable name="CachingIconView">
-        <!-- Maximum width of displayed drawable. Drawables exceeding this size will be downsampled. -->
-        <attr name="maxDrawableWidth" format="dimension"/>
-        <!-- Maximum width of height drawable. Drawables exceeding this size will be downsampled. -->
-        <attr name="maxDrawableHeight" format="dimension"/>
-    </declare-styleable>
     </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 269aa1b..62b3054 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4857,6 +4857,7 @@
         <item>0.25</item>
         <item>0.5</item>
         <item>0.75</item>
+        <item>0.875</item>
     </string-array>
 
     <!-- Messages that should not be shown to the user during face auth enrollment. This should be
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 0a4c4c0..2dc17b8 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -148,10 +148,6 @@
     <public name="supportsInlineSuggestionsWithTouchExploration" />
     <public name="lineBreakStyle" />
     <public name="lineBreakWordStyle" />
-    <!-- @hide -->
-    <public name="maxDrawableWidth" />
-    <!-- @hide -->
-    <public name="maxDrawableHeight" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/tests/coretests/res/drawable/big_a.png b/core/tests/coretests/res/drawable/big_a.png
deleted file mode 100644
index dc059a3..0000000
--- a/core/tests/coretests/res/drawable/big_a.png
+++ /dev/null
Binary files differ
diff --git a/core/tests/coretests/res/layout/caching_icon_view_test_max_size.xml b/core/tests/coretests/res/layout/caching_icon_view_test_max_size.xml
deleted file mode 100644
index 9a03446..0000000
--- a/core/tests/coretests/res/layout/caching_icon_view_test_max_size.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<com.android.internal.widget.CachingIconView
-          xmlns:android="http://schemas.android.com/apk/res/android"
-          android:id="@+id/caching_icon_view"
-          android:layout_width="120dp"
-          android:layout_height="120dp"
-          android:maxDrawableWidth="80dp"
-          android:maxDrawableHeight="80dp" />
diff --git a/core/tests/coretests/res/layout/caching_icon_view_test_no_max_size.xml b/core/tests/coretests/res/layout/caching_icon_view_test_no_max_size.xml
deleted file mode 100644
index a213a97..0000000
--- a/core/tests/coretests/res/layout/caching_icon_view_test_no_max_size.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<com.android.internal.widget.CachingIconView
-          xmlns:android="http://schemas.android.com/apk/res/android"
-          android:id="@+id/caching_icon_view"
-          android:layout_width="120dp"
-          android:layout_height="120dp" />
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index 1ae9649..e303934 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -60,7 +60,7 @@
 @RunWith(AndroidJUnit4.class)
 public class HandwritingInitiatorTest {
     private static final int TOUCH_SLOP = 8;
-    private static final long TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
+    private static final long TIMEOUT = ViewConfiguration.getLongPressTimeout();
     private static final Rect sHwArea = new Rect(100, 200, 500, 500);
 
     private HandwritingInitiator mHandwritingInitiator;
@@ -177,7 +177,7 @@
     }
 
     @Test
-    public void onTouchEvent_notStartHandwriting_when_stylusMove_afterTapTimeOut() {
+    public void onTouchEvent_notStartHandwriting_when_stylusMove_afterTimeOut() {
         mHandwritingInitiator.onInputConnectionCreated(mTestView);
         final int x1 = 10;
         final int y1 = 10;
@@ -187,7 +187,7 @@
 
         final int x2 = x1 + TOUCH_SLOP * 2;
         final int y2 = y1;
-        final long time2 = time1 + TAP_TIMEOUT + 10L;
+        final long time2 = time1 + TIMEOUT + 10L;
         MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, time2);
         mHandwritingInitiator.onTouchEvent(stylusEvent2);
 
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index ec45a01..625f52a 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -232,7 +232,7 @@
         assertThat(entry3.handlerClassName).isEqualTo(
                 "com.android.internal.os.LooperStatsTest$TestHandlerSecond");
         assertThat(entry3.messageName).startsWith(
-                "com.android.internal.os.LooperStatsTest$$ExternalSyntheticLambda5");
+                "com.android.internal.os.LooperStatsTest$$ExternalSyntheticLambda");
         assertThat(entry3.messageCount).isEqualTo(1);
         assertThat(entry3.recordedMessageCount).isEqualTo(1);
         assertThat(entry3.exceptionCount).isEqualTo(0);
diff --git a/core/tests/coretests/src/com/android/internal/widget/CachingIconViewTest.java b/core/tests/coretests/src/com/android/internal/widget/CachingIconViewTest.java
deleted file mode 100644
index 0d4b449..0000000
--- a/core/tests/coretests/src/com/android/internal/widget/CachingIconViewTest.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.graphics.drawable.InsetDrawable;
-import android.net.Uri;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.frameworks.coretests.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class CachingIconViewTest {
-
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-    }
-
-    @Test
-    public void customDrawable_setImageIcon_skipsResizeSuccessfully() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageIcon(Icon.createWithResource(mContext, R.drawable.custom_drawable));
-        Drawable drawable = view.getDrawable();
-        assertThat(drawable).isInstanceOf(InsetDrawable.class);
-    }
-
-    @Test
-    public void customDrawable_setImageIconAsync_skipsResizeSuccessfully() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageIconAsync(Icon.createWithResource(mContext, R.drawable.custom_drawable)).run();
-        Drawable drawable = view.getDrawable();
-        assertThat(drawable).isInstanceOf(InsetDrawable.class);
-    }
-
-    @Test
-    public void customDrawable_setImageResource_skipsResizeSuccessfully() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageResource(R.drawable.custom_drawable);
-        Drawable drawable = view.getDrawable();
-        assertThat(drawable).isInstanceOf(InsetDrawable.class);
-    }
-
-    @Test
-    public void customDrawable_setImageResourceAsync_skipsResizeSuccessfully() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageResourceAsync(R.drawable.custom_drawable).run();
-        Drawable drawable = view.getDrawable();
-        assertThat(drawable).isInstanceOf(InsetDrawable.class);
-    }
-
-    @Test
-    public void customDrawable_setImageUri_skipsResizeSuccessfully() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageURI(Uri.parse(
-                "android.resource://com.android.frameworks.coretests/"
-                        + R.drawable.custom_drawable));
-        Drawable drawable = view.getDrawable();
-        assertThat(drawable).isInstanceOf(InsetDrawable.class);
-    }
-
-    @Test
-    public void customDrawable_setImageUriAsync_skipsResizeSuccessfully() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageURIAsync(Uri.parse(
-                "android.resource://com.android.frameworks.coretests/"
-                        + R.drawable.custom_drawable)).run();
-        Drawable drawable = view.getDrawable();
-        assertThat(drawable).isInstanceOf(InsetDrawable.class);
-    }
-
-    @Test
-    public void maxDrawableDimensionsSet_setImageIcon_resizesImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageIcon(Icon.createWithResource(mContext, R.drawable.big_a));
-
-        assertDrawableResized(view);
-    }
-
-    @Test
-    public void maxDrawableWithNoDimensionsSet_setImageIcon_doesNotResizeImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_no_max_size, null);
-        view.setImageIcon(Icon.createWithResource(mContext, R.drawable.big_a));
-
-        assertDrawableNotResized(view);
-    }
-
-    @Test
-    public void maxDrawableDimensionsSet_setImageIconAsync_resizesImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageIconAsync(Icon.createWithResource(mContext, R.drawable.big_a)).run();
-
-        assertDrawableResized(view);
-    }
-
-    @Test
-    public void maxDrawableWithNoDimensionsSet_setImageIconAsync_doesNotResizeImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_no_max_size, null);
-        view.setImageIconAsync(Icon.createWithResource(mContext, R.drawable.big_a)).run();
-
-        assertDrawableNotResized(view);
-    }
-
-    @Test
-    public void maxDrawableDimensionsSet_setImageResource_resizesImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageResource(R.drawable.big_a);
-
-        assertDrawableResized(view);
-    }
-
-    @Test
-    public void maxDrawableWithNoDimensionsSet_setImageResource_doesNotResizeImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_no_max_size, null);
-        view.setImageResource(R.drawable.big_a);
-
-        assertDrawableNotResized(view);
-    }
-
-    @Test
-    public void maxDrawableDimensionsSet_setImageResourceAsync_resizesImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageResourceAsync(R.drawable.big_a).run();
-
-        assertDrawableResized(view);
-    }
-
-    @Test
-    public void maxDrawableWithNoDimensionsSet_setImageResourceAsync_doesNotResizeImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_no_max_size, null);
-        view.setImageResourceAsync(R.drawable.big_a).run();
-
-        assertDrawableNotResized(view);
-    }
-
-    @Test
-    public void maxDrawableDimensionsSet_setImageUri_resizesImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageURI(Uri.parse(
-                "android.resource://com.android.frameworks.coretests/" + R.drawable.big_a));
-
-        assertDrawableResized(view);
-    }
-
-    @Test
-    public void maxDrawableWithNoDimensionsSet_setImageUri_doesNotResizeImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_no_max_size, null);
-        view.setImageURI(Uri.parse(
-                "android.resource://com.android.frameworks.coretests/" + R.drawable.big_a));
-
-        assertDrawableNotResized(view);
-    }
-
-    @Test
-    public void maxDrawableDimensionsSet_setImageUriAsync_resizesImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_max_size, null);
-        view.setImageURIAsync(Uri.parse(
-                "android.resource://com.android.frameworks.coretests/" + R.drawable.big_a)).run();
-
-        assertDrawableResized(view);
-    }
-
-    @Test
-    public void maxDrawableWithNoDimensionsSet_setImageUriAsync_doesNotResizeImageIcon() {
-        CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
-                R.layout.caching_icon_view_test_no_max_size, null);
-        view.setImageURIAsync(Uri.parse(
-                "android.resource://com.android.frameworks.coretests/" + R.drawable.big_a)).run();
-
-        assertDrawableNotResized(view);
-    }
-
-
-    private void assertDrawableResized(@Nullable CachingIconView view) {
-        assertThat(view).isNotNull();
-        int maxSize =
-                (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80f,
-                        mContext.getResources().getDisplayMetrics());
-        assertThat(view.getMaxDrawableHeight()).isEqualTo(maxSize);
-        assertThat(view.getMaxDrawableWidth()).isEqualTo(maxSize);
-
-        Drawable drawable = view.getDrawable();
-        assertThat(drawable).isInstanceOf(BitmapDrawable.class);
-        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
-        assertThat(bitmapDrawable.getBitmap().getWidth()).isLessThan(maxSize + 1);
-        assertThat(bitmapDrawable.getBitmap().getHeight()).isLessThan(maxSize + 1);
-    }
-
-    private void assertDrawableNotResized(@Nullable CachingIconView view) {
-        assertThat(view).isNotNull();
-        int maxSize =
-                (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80f,
-                        mContext.getResources().getDisplayMetrics());
-        assertThat(view.getMaxDrawableHeight()).isEqualTo(-1);
-        assertThat(view.getMaxDrawableWidth()).isEqualTo(-1);
-
-        Drawable drawable = view.getDrawable();
-        assertThat(drawable).isInstanceOf(BitmapDrawable.class);
-        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
-        assertThat(bitmapDrawable.getBitmap().getWidth()).isGreaterThan(maxSize);
-        assertThat(bitmapDrawable.getBitmap().getHeight()).isGreaterThan(maxSize);
-    }
-}
diff --git a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
deleted file mode 100644
index 8dcb4a2..0000000
--- a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import android.content.Context;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-
-import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.frameworks.coretests.R;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-
-@RunWith(AndroidJUnit4ClassRunner.class)
-public class LocalImageResolverTest {
-
-    private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
-
-    @Test
-    public void resolveImage_largeBitmapIcon_defaultSize_resizeToDefaultSize() throws
-            IOException {
-        Icon icon = Icon.createWithBitmap(
-                BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a));
-        Drawable d = LocalImageResolver.resolveImage(icon, mContext);
-
-        assertThat(d).isInstanceOf(BitmapDrawable.class);
-        BitmapDrawable bd = (BitmapDrawable) d;
-        // No isLessOrEqualThan sadly.
-        assertThat(bd.getBitmap().getWidth()).isLessThan(
-                LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
-        assertThat(bd.getBitmap().getHeight()).isLessThan(
-                LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
-    }
-
-    @Test
-    public void resolveImage_largeAdaptiveBitmapIcon_defaultSize_resizeToDefaultSize() throws
-            IOException {
-        Icon icon = Icon.createWithAdaptiveBitmap(
-                BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a));
-        Drawable d = LocalImageResolver.resolveImage(icon, mContext);
-
-        assertThat(d).isInstanceOf(AdaptiveIconDrawable.class);
-        BitmapDrawable bd = (BitmapDrawable) ((AdaptiveIconDrawable) d).getForeground();
-        // No isLessOrEqualThan sadly.
-        assertThat(bd.getBitmap().getWidth()).isLessThan(
-                LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
-        assertThat(bd.getBitmap().getHeight()).isLessThan(
-                LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
-    }
-
-    @Test
-    public void resolveImage_largeResourceIcon_defaultSize_resizeToDefaultSize() throws
-            IOException {
-        Icon icon = Icon.createWithResource(mContext, R.drawable.big_a);
-        Drawable d = LocalImageResolver.resolveImage(icon, mContext);
-
-        assertThat(d).isInstanceOf(BitmapDrawable.class);
-        BitmapDrawable bd = (BitmapDrawable) d;
-        // No isLessOrEqualThan sadly.
-        assertThat(bd.getBitmap().getWidth()).isLessThan(
-                LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
-        assertThat(bd.getBitmap().getHeight()).isLessThan(
-                LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
-    }
-
-    @Test
-    public void resolveImage_largeResourceIcon_passedSize_resizeToDefinedSize() throws
-            IOException {
-        Icon icon = Icon.createWithResource(mContext, R.drawable.big_a);
-        Drawable d = LocalImageResolver.resolveImage(icon, mContext, 100, 50);
-
-        assertThat(d).isInstanceOf(BitmapDrawable.class);
-        BitmapDrawable bd = (BitmapDrawable) d;
-        assertThat(bd.getBitmap().getWidth()).isLessThan(101);
-        assertThat(bd.getBitmap().getHeight()).isLessThan(51);
-    }
-
-    @Test
-    public void resolveImage_largeBitmapIcon_passedSize_resizeToDefinedSize() throws
-            IOException {
-        Icon icon = Icon.createWithBitmap(
-                BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a));
-        Drawable d = LocalImageResolver.resolveImage(icon, mContext, 100, 50);
-
-        assertThat(d).isInstanceOf(BitmapDrawable.class);
-        BitmapDrawable bd = (BitmapDrawable) d;
-        assertThat(bd.getBitmap().getWidth()).isLessThan(101);
-        assertThat(bd.getBitmap().getHeight()).isLessThan(51);
-    }
-
-    @Test
-    public void resolveImage_largeAdaptiveBitmapIcon_passedSize_resizeToDefinedSize() throws
-            IOException {
-        Icon icon = Icon.createWithAdaptiveBitmap(
-                BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a));
-        Drawable d = LocalImageResolver.resolveImage(icon, mContext, 100, 50);
-
-        assertThat(d).isInstanceOf(AdaptiveIconDrawable.class);
-        BitmapDrawable bd = (BitmapDrawable) ((AdaptiveIconDrawable) d).getForeground();
-        assertThat(bd.getBitmap().getWidth()).isLessThan(101);
-        assertThat(bd.getBitmap().getHeight()).isLessThan(51);
-    }
-
-
-    @Test
-    public void resolveImage_smallResourceIcon_defaultSize_untouched() throws IOException {
-        Icon icon = Icon.createWithResource(mContext, R.drawable.test32x24);
-        Drawable d = LocalImageResolver.resolveImage(icon, mContext);
-
-        assertThat(d).isInstanceOf(BitmapDrawable.class);
-        BitmapDrawable bd = (BitmapDrawable) d;
-        assertThat(bd.getBitmap().getWidth()).isEqualTo(32);
-        assertThat(bd.getBitmap().getHeight()).isEqualTo(24);
-    }
-
-    @Test
-    public void resolveImage_smallBitmapIcon_defaultSize_untouched() throws IOException {
-        Icon icon = Icon.createWithBitmap(
-                BitmapFactory.decodeResource(mContext.getResources(), R.drawable.test32x24));
-        final int originalWidth = icon.getBitmap().getWidth();
-        final int originalHeight = icon.getBitmap().getHeight();
-
-        Drawable d = LocalImageResolver.resolveImage(icon, mContext);
-
-        assertThat(d).isInstanceOf(BitmapDrawable.class);
-        BitmapDrawable bd = (BitmapDrawable) d;
-        assertThat(bd.getBitmap().getWidth()).isEqualTo(originalWidth);
-        assertThat(bd.getBitmap().getHeight()).isEqualTo(originalHeight);
-    }
-
-    @Test
-    public void resolveImage_smallAdaptiveBitmapIcon_defaultSize_untouched() throws IOException {
-        Icon icon = Icon.createWithAdaptiveBitmap(
-                BitmapFactory.decodeResource(mContext.getResources(), R.drawable.test32x24));
-        final int originalWidth = icon.getBitmap().getWidth();
-        final int originalHeight = icon.getBitmap().getHeight();
-
-        Drawable d = LocalImageResolver.resolveImage(icon, mContext);
-        assertThat(d).isInstanceOf(AdaptiveIconDrawable.class);
-        BitmapDrawable bd = (BitmapDrawable) ((AdaptiveIconDrawable) d).getForeground();
-        assertThat(bd.getBitmap().getWidth()).isEqualTo(originalWidth);
-        assertThat(bd.getBitmap().getHeight()).isEqualTo(originalHeight);
-
-    }
-}
diff --git a/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
index 5dc44d2..8de9196 100644
--- a/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
+++ b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
@@ -123,7 +123,7 @@
     public void testLogDuration() throws Exception {
         TimingsTraceLog log = new TimingsTraceLog(TAG, TRACE_TAG_APP, 10);
         log.logDuration("logro", 42);
-        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), contains("logro took to complete: 42ms")));
+        verify((MockedVoidMethod) () -> Slog.v(eq(TAG), contains("logro took to complete: 42ms")));
     }
 
     @Test
@@ -134,7 +134,7 @@
 
         verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "test"));
         verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP));
-        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("test took to complete: \\dms")));
+        verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("test took to complete: \\dms")));
     }
 
     @Test
@@ -149,8 +149,8 @@
         verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L2"));
         verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP), times(2)); // L1 and L2
 
-        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L2 took to complete: \\d+ms")));
-        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L1 took to complete: \\d+ms")));
+        verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L2 took to complete: \\d+ms")));
+        verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L1 took to complete: \\d+ms")));
     }
 
     @Test
@@ -170,9 +170,9 @@
         verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L3"));
         verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP), times(3));
 
-        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L2 took to complete: \\d+ms")));
-        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L1 took to complete: \\d+ms")));
-        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L3 took to complete: \\d+ms")),
+        verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L2 took to complete: \\d+ms")));
+        verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L1 took to complete: \\d+ms")));
+        verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L3 took to complete: \\d+ms")),
                 never());
 
         verify((MockedVoidMethod) () -> Slog.w(TAG, "not tracing duration of 'L3' "
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 2a82d8e..df51871 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -71,14 +71,6 @@
 }
 
 prebuilt_etc {
-    name: "privapp_whitelist_com.android.cellbroadcastreceiver",
-    system_ext_specific: true,
-    sub_dir: "permissions",
-    src: "com.android.cellbroadcastreceiver.xml",
-    filename_from_src: true,
-}
-
-prebuilt_etc {
     name: "privapp_whitelist_com.android.contacts",
     product_specific: true,
     sub_dir: "permissions",
diff --git a/data/etc/com.android.cellbroadcastreceiver.xml b/data/etc/com.android.cellbroadcastreceiver.xml
deleted file mode 100644
index bc62bbc..0000000
--- a/data/etc/com.android.cellbroadcastreceiver.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<permissions>
-    <privapp-permissions package="com.android.cellbroadcastreceiver">
-        <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
-        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
-        <permission name="android.permission.MANAGE_USERS"/>
-        <permission name="android.permission.STATUS_BAR"/>
-        <permission name="android.permission.MODIFY_PHONE_STATE"/>
-        <permission name="android.permission.MODIFY_CELL_BROADCASTS"/>
-        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
-        <permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/>
-        <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
-    </privapp-permissions>
-</permissions>
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 2d1db71..d1873a0 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -76,8 +76,5 @@
         <permission name="android.permission.FORCE_STOP_PACKAGES" />
         <permission name="android.permission.ACCESS_FPS_COUNTER" />
         <permission name="android.permission.CHANGE_CONFIGURATION" />
-        <permission name="android.permission.LOG_COMPAT_CHANGE" />
-        <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
-        <permission name="android.permission.READ_DEVICE_CONFIG" />
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 3e91eed..5628548 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -38,24 +38,6 @@
         <permission name="android.permission.CRYPT_KEEPER"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.cellbroadcastreceiver.module">
-        <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
-        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
-        <permission name="android.permission.MANAGE_USERS"/>
-        <permission name="android.permission.STATUS_BAR"/>
-        <permission name="android.permission.MODIFY_PHONE_STATE"/>
-        <permission name="android.permission.MODIFY_CELL_BROADCASTS"/>
-        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
-        <permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/>
-        <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
-    </privapp-permissions>
-
-    <privapp-permissions package="com.android.cellbroadcastservice">
-        <permission name="android.permission.MODIFY_PHONE_STATE"/>
-        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
-        <permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.externalstorage">
         <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index e898f57..4449c48 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2257,6 +2257,12 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/TransitionController.java"
     },
+    "264036181": {
+      "message": "Unable to retrieve task to start recording for display %d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONTENT_RECORDING",
+      "at": "com\/android\/server\/wm\/ContentRecorder.java"
+    },
     "269576220": {
       "message": "Resuming rotation after drag",
       "level": "DEBUG",
@@ -2761,6 +2767,12 @@
       "group": "WM_DEBUG_WALLPAPER",
       "at": "com\/android\/server\/wm\/WallpaperWindowToken.java"
     },
+    "736003885": {
+      "message": "Unable to retrieve the task token to start recording for display %d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONTENT_RECORDING",
+      "at": "com\/android\/server\/wm\/ContentRecorder.java"
+    },
     "736692676": {
       "message": "Config is relaunching %s",
       "level": "VERBOSE",
@@ -2791,6 +2803,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
+    "778774915": {
+      "message": "Unable to record task since feature is disabled %d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONTENT_RECORDING",
+      "at": "com\/android\/server\/wm\/ContentRecorder.java"
+    },
     "781471998": {
       "message": "moveWindowTokenToDisplay: Cannot move to the original display for token: %s",
       "level": "WARN",
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 7e68bc0..1a522bd 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -291,6 +291,14 @@
     }
 
     /**
+     * @return {@code true} if the rectangle is valid (left <= right and top <= bottom).
+     * @hide
+     */
+    public boolean isValid() {
+        return left <= right && top <= bottom;
+    }
+
+    /**
      * @return the rectangle's width. This does not check for a valid rectangle
      * (i.e. left <= right) so the result may be negative.
      */
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 61f7fac..a2f5301 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -329,7 +329,7 @@
             FontFamily.Builder familyBuilder = null;
             for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
                 final Font.Builder fontBuilder = new Font.Builder(mgr, fontFile.getFileName(),
-                        false /* isAsset */, 0 /* cookie */)
+                        false /* isAsset */, AssetManager.COOKIE_UNKNOWN)
                         .setTtcIndex(fontFile.getTtcIndex())
                         .setFontVariationSettings(fontFile.getVariationSettings());
                 if (fontFile.getWeight() != Typeface.RESOLVE_BY_FONT_TABLE) {
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index cd7936d..abd0be9 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -179,7 +179,7 @@
          */
         public Builder(@NonNull AssetManager am, @NonNull String path) {
             try {
-                mBuffer = createBuffer(am, path, true /* is asset */, 0 /* cookie */);
+                mBuffer = createBuffer(am, path, true /* is asset */, AssetManager.COOKIE_UNKNOWN);
             } catch (IOException e) {
                 mException = e;
             }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 1d2b938..2aa6953 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -501,7 +501,7 @@
         final  TaskFragmentContainer container = getContainerWithActivity(
                 activity.getActivityToken());
         // Don't launch placeholder if the container is occluded.
-        if (container != getTopActiveContainer()) {
+        if (container != null && container != getTopActiveContainer()) {
             return false;
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 5246117..daba774 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -532,7 +532,9 @@
     }
 
     private int getSmallestWidthDp(Rect bounds) {
-        final int minWidth = Math.min(bounds.width(), bounds.height());
+        mTempRect.set(bounds);
+        mTempRect.inset(getDisplayInsets(mContext));
+        final int minWidth = Math.min(mTempRect.width(), mTempRect.height());
         final float density = mContext.getResources().getDisplayMetrics().density;
         return (int) (minWidth / density);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index b2bbafe..99b32a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -42,6 +42,7 @@
 import com.android.wm.shell.common.annotations.ExternalThread;
 import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
 import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
+import com.android.wm.shell.transition.Transitions;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -50,6 +51,8 @@
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
+import dagger.Lazy;
+
 /**
  * Controller to show/update compat UI components on Tasks based on whether the foreground
  * activities are in compatibility mode.
@@ -102,6 +105,7 @@
     private final DisplayImeController mImeController;
     private final SyncTransactionQueue mSyncQueue;
     private final ShellExecutor mMainExecutor;
+    private final Lazy<Transitions> mTransitionsLazy;
     private final CompatUIImpl mImpl = new CompatUIImpl();
 
     private CompatUICallback mCallback;
@@ -118,13 +122,15 @@
             DisplayInsetsController displayInsetsController,
             DisplayImeController imeController,
             SyncTransactionQueue syncQueue,
-            ShellExecutor mainExecutor) {
+            ShellExecutor mainExecutor,
+            Lazy<Transitions> transitionsLazy) {
         mContext = context;
         mDisplayController = displayController;
         mDisplayInsetsController = displayInsetsController;
         mImeController = imeController;
         mSyncQueue = syncQueue;
         mMainExecutor = mainExecutor;
+        mTransitionsLazy = transitionsLazy;
         mDisplayController.addDisplayWindowListener(this);
         mImeController.addPositionProcessor(this);
         mCompatUIHintsState = new CompatUIHintsState();
@@ -302,6 +308,7 @@
             ShellTaskOrganizer.TaskListener taskListener) {
         return new LetterboxEduWindowManager(context, taskInfo,
                 mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
+                mTransitionsLazy.get(),
                 this::onLetterboxEduDismissed);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
index 03986ee..3061eab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
+import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -42,7 +43,9 @@
 class LetterboxEduAnimationController {
     private static final String TAG = "LetterboxEduAnimation";
 
-    private static final int ENTER_ANIM_START_DELAY_MILLIS = 500;
+    // If shell transitions are enabled, startEnterAnimation will be called after all transitions
+    // have finished, and therefore the start delay should be shorter.
+    private static final int ENTER_ANIM_START_DELAY_MILLIS = ENABLE_SHELL_TRANSITIONS ? 300 : 500;
 
     private final TransitionAnimation mTransitionAnimation;
     private final String mPackageName;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
index 8aa4d0e..2e0b09e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
@@ -38,6 +38,7 @@
     // The alpha of a background is a number between 0 (fully transparent) to 255 (fully opaque).
     // 204 is simply 255 * 0.8.
     static final int BACKGROUND_DIM_ALPHA = 204;
+
     private View mDialogContainer;
     private TextView mDialogTitle;
     private Drawable mBackgroundDim;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
index dda72ff..cc3a3b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
@@ -36,6 +36,7 @@
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.CompatUIWindowManagerAbstract;
+import com.android.wm.shell.transition.Transitions;
 
 /**
  * Window manager for the Letterbox Education.
@@ -63,6 +64,8 @@
 
     private final LetterboxEduAnimationController mAnimationController;
 
+    private final Transitions mTransitions;
+
     // Remember the last reported state in case visibility changes due to keyguard or IME updates.
     private boolean mEligibleForLetterboxEducation;
 
@@ -80,17 +83,19 @@
 
     public LetterboxEduWindowManager(Context context, TaskInfo taskInfo,
             SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
-            DisplayLayout displayLayout, Runnable onDismissCallback) {
-        this(context, taskInfo, syncQueue, taskListener, displayLayout, onDismissCallback,
-                new LetterboxEduAnimationController(context));
+            DisplayLayout displayLayout, Transitions transitions,
+            Runnable onDismissCallback) {
+        this(context, taskInfo, syncQueue, taskListener, displayLayout, transitions,
+                onDismissCallback, new LetterboxEduAnimationController(context));
     }
 
     @VisibleForTesting
     LetterboxEduWindowManager(Context context, TaskInfo taskInfo,
             SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
-            DisplayLayout displayLayout, Runnable onDismissCallback,
+            DisplayLayout displayLayout, Transitions transitions, Runnable onDismissCallback,
             LetterboxEduAnimationController animationController) {
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
+        mTransitions = transitions;
         mOnDismissCallback = onDismissCallback;
         mAnimationController = animationController;
         mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation;
@@ -132,8 +137,8 @@
         mLayout = inflateLayout();
         updateDialogMargins();
 
-        mAnimationController.startEnterAnimation(mLayout, /* endCallback= */
-                this::onDialogEnterAnimationEnded);
+        // startEnterAnimation will be called immediately if shell-transitions are disabled.
+        mTransitions.runOnIdle(this::startEnterAnimation);
 
         return mLayout;
     }
@@ -158,8 +163,18 @@
                 R.layout.letterbox_education_dialog_layout, null);
     }
 
+    private void startEnterAnimation() {
+        if (mLayout == null) {
+            // Dialog has already been released.
+            return;
+        }
+        mAnimationController.startEnterAnimation(mLayout, /* endCallback= */
+                this::onDialogEnterAnimationEnded);
+    }
+
     private void onDialogEnterAnimationEnded() {
         if (mLayout == null) {
+            // Dialog has already been released.
             return;
         }
         mLayout.setDismissOnClickListener(this::onDismiss);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index bf0337d..1ee9407 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -96,6 +96,7 @@
 import java.util.Optional;
 
 import dagger.BindsOptionalOf;
+import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
 
@@ -210,9 +211,9 @@
     static CompatUIController provideCompatUIController(Context context,
             DisplayController displayController, DisplayInsetsController displayInsetsController,
             DisplayImeController imeController, SyncTransactionQueue syncQueue,
-            @ShellMainThread ShellExecutor mainExecutor) {
+            @ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy) {
         return new CompatUIController(context, displayController, displayInsetsController,
-                imeController, syncQueue, mainExecutor);
+                imeController, syncQueue, mainExecutor, transitionsLazy);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index c2d5823..7397e52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -194,7 +194,7 @@
     public float getAspectRatioOrDefault(
             @android.annotation.Nullable PictureInPictureParams params) {
         return params != null && params.hasSetAspectRatio()
-                ? params.getAspectRatio()
+                ? params.getAspectRatioFloat()
                 : getDefaultAspectRatio();
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index c1e7825..5d6b041 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1071,13 +1071,13 @@
      */
     private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) {
         final Rational currentAspectRatio =
-                mPictureInPictureParams != null ? mPictureInPictureParams.getAspectRatioRational()
+                mPictureInPictureParams != null ? mPictureInPictureParams.getAspectRatio()
                         : null;
         final boolean aspectRatioChanged = !Objects.equals(currentAspectRatio,
-                params.getAspectRatioRational());
+                params.getAspectRatio());
         mPictureInPictureParams = params;
         if (aspectRatioChanged) {
-            mPipBoundsState.setAspectRatio(params.getAspectRatio());
+            mPipBoundsState.setAspectRatio(params.getAspectRatioFloat());
         }
         return aspectRatioChanged;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index d880f82..9865548 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -75,7 +75,7 @@
     public void setBoundsStateForEntry(ComponentName componentName, ActivityInfo activityInfo,
             PictureInPictureParams params, PipBoundsAlgorithm pipBoundsAlgorithm) {
         super.setBoundsStateForEntry(componentName, activityInfo, params, pipBoundsAlgorithm);
-        setDesiredTvExpandedAspectRatio(params.getExpandedAspectRatio(), true);
+        setDesiredTvExpandedAspectRatio(params.getExpandedAspectRatioFloat(), true);
     }
 
     /** Resets the TV PiP state for a new activity. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index e88eef9..177b4a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -24,6 +24,7 @@
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -32,6 +33,7 @@
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
@@ -68,6 +70,7 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.common.split.SplitLayout;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.draganddrop.DragAndDropPolicy;
 import com.android.wm.shell.recents.RecentTasksController;
@@ -196,11 +199,12 @@
 
     @Nullable
     public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
-        if (isSplitScreenVisible()) {
-            int taskId = mStageCoordinator.getTaskId(splitPosition);
-            return mTaskOrganizer.getRunningTaskInfo(taskId);
+        if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
+            return null;
         }
-        return null;
+
+        final int taskId = mStageCoordinator.getTaskId(splitPosition);
+        return mTaskOrganizer.getRunningTaskInfo(taskId);
     }
 
     public boolean isTaskInSplitScreen(int taskId) {
@@ -350,6 +354,18 @@
                     IRemoteAnimationFinishedCallback finishedCallback,
                     SurfaceControl.Transaction t) {
                 if (apps == null || apps.length == 0) {
+                    final ActivityManager.RunningTaskInfo pairedTaskInfo =
+                            getTaskInfo(SplitLayout.reversePosition(position));
+                    final ComponentName pairedActivity =
+                            pairedTaskInfo != null ? pairedTaskInfo.baseActivity : null;
+                    final ComponentName intentActivity =
+                            intent.getIntent() != null ? intent.getIntent().getComponent() : null;
+                    if (pairedActivity != null && pairedActivity.equals(intentActivity)) {
+                        // Switch split position if dragging the same activity to another side.
+                        setSideStagePosition(SplitLayout.reversePosition(
+                                mStageCoordinator.getSideStagePosition()));
+                    }
+
                     // Do nothing when the animation was cancelled.
                     t.apply();
                     return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index fb3cd87..435d670 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -109,6 +109,9 @@
     /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
     private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
 
+    /** List of {@link Runnable} instances to run when the last active transition has finished.  */
+    private final ArrayList<Runnable> mRunWhenIdleQueue = new ArrayList<>();
+
     private float mTransitionAnimationScaleSetting = 1.0f;
 
     private static final class ActiveTransition {
@@ -224,6 +227,21 @@
         mRemoteTransitionHandler.removeFiltered(remoteTransition);
     }
 
+    /**
+     * Runs the given {@code runnable} when the last active transition has finished, or immediately
+     * if there are currently no active transitions.
+     *
+     * <p>This method should be called on the Shell main-thread, where the given {@code runnable}
+     * will be executed when the last active transition is finished.
+     */
+    public void runOnIdle(Runnable runnable) {
+        if (mActiveTransitions.isEmpty()) {
+            runnable.run();
+        } else {
+            mRunWhenIdleQueue.add(runnable);
+        }
+    }
+
     /** @return true if the transition was triggered by opening something vs closing something */
     public static boolean isOpeningType(@WindowManager.TransitionType int type) {
         return type == TRANSIT_OPEN
@@ -520,6 +538,11 @@
         if (mActiveTransitions.size() <= activeIdx) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
                     + "finished");
+            // Run all runnables from the run-when-idle queue.
+            for (int i = 0; i < mRunWhenIdleQueue.size(); i++) {
+                mRunWhenIdleQueue.get(i).run();
+            }
+            mRunWhenIdleQueue.clear();
             return;
         }
         // Start animating the next active transition
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index a31b287..4607d8a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -53,6 +53,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
+import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,6 +63,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import dagger.Lazy;
+
 /**
  * Tests for {@link CompatUIController}.
  *
@@ -82,6 +85,7 @@
     private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
     private @Mock SyncTransactionQueue mMockSyncQueue;
     private @Mock ShellExecutor mMockExecutor;
+    private @Mock Lazy<Transitions> mMockTransitionsLazy;
     private @Mock CompatUIWindowManager mMockCompatLayout;
     private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout;
 
@@ -102,7 +106,8 @@
         doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
         doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
         mController = new CompatUIController(mContext, mMockDisplayController,
-                mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
+                mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor,
+                mMockTransitionsLazy) {
             @Override
             CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
                     ShellTaskOrganizer.TaskListener taskListener) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
index 337b738..7d51b52 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
@@ -54,6 +54,7 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.transition.Transitions;
 
 import org.junit.After;
 import org.junit.Before;
@@ -86,11 +87,14 @@
     private ArgumentCaptor<WindowManager.LayoutParams> mWindowAttrsCaptor;
     @Captor
     private ArgumentCaptor<Runnable> mEndCallbackCaptor;
+    @Captor
+    private ArgumentCaptor<Runnable> mRunOnIdleCaptor;
 
     @Mock private LetterboxEduAnimationController mAnimationController;
     @Mock private SyncTransactionQueue mSyncTransactionQueue;
     @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
     @Mock private SurfaceControlViewHost mViewHost;
+    @Mock private Transitions mTransitions;
     @Mock private Runnable mOnDismissCallback;
 
     private SharedPreferences mSharedPreferences;
@@ -204,6 +208,23 @@
     }
 
     @Test
+    public void testCreateLayout_windowManagerReleasedBeforeTransitionsIsIdle_doesNotStartAnim() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ true));
+
+        assertTrue(mSharedPreferences.getBoolean(mPrefKey, /* default= */ false));
+
+        verify(mTransitions).runOnIdle(mRunOnIdleCaptor.capture());
+
+        windowManager.release();
+
+        mRunOnIdleCaptor.getValue().run();
+
+        verify(mAnimationController, never()).startEnterAnimation(any(), any());
+    }
+
+    @Test
     public void testUpdateCompatInfo_updatesLayoutCorrectly() {
         LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
 
@@ -303,6 +324,13 @@
     }
 
     private void verifyAndFinishEnterAnimation(LetterboxEduDialogLayout layout) {
+        verify(mTransitions).runOnIdle(mRunOnIdleCaptor.capture());
+
+        // startEnterAnimation isn't called until run-on-idle runnable is called.
+        verify(mAnimationController, never()).startEnterAnimation(any(), any());
+
+        mRunOnIdleCaptor.getValue().run();
+
         verify(mAnimationController).startEnterAnimation(eq(layout), mEndCallbackCaptor.capture());
         mEndCallbackCaptor.getValue().run();
     }
@@ -320,7 +348,8 @@
             boolean isTaskbarEduShowing) {
         LetterboxEduWindowManager windowManager = new LetterboxEduWindowManager(mContext,
                 createTaskInfo(eligible), mSyncTransactionQueue, mTaskListener,
-                createDisplayLayout(), mOnDismissCallback, mAnimationController);
+                createDisplayLayout(), mTransitions, mOnDismissCallback,
+                mAnimationController);
 
         spyOn(windowManager);
         doReturn(mViewHost).when(windowManager).createSurfaceViewHost();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index dbf93b4..a0b1297 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -46,6 +46,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -93,7 +94,7 @@
  * Tests for the shell transitions.
  *
  * Build/Install/Run:
- *  atest WMShellUnitTests:ShellTransitionTests
+ * atest WMShellUnitTests:ShellTransitionTests
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -600,6 +601,83 @@
         assertTrue(DefaultTransitionHandler.isRotationSeamless(seamlessDisplay, displays));
     }
 
+    @Test
+    public void testRunWhenIdle() {
+        Transitions transitions = createTestTransitions();
+        transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+        Runnable runnable1 = mock(Runnable.class);
+        Runnable runnable2 = mock(Runnable.class);
+        Runnable runnable3 = mock(Runnable.class);
+        Runnable runnable4 = mock(Runnable.class);
+
+        transitions.runOnIdle(runnable1);
+
+        // runnable1 is executed immediately because there are no active transitions.
+        verify(runnable1, times(1)).run();
+
+        clearInvocations(runnable1);
+
+        IBinder transitToken1 = new Binder();
+        transitions.requestStartTransition(transitToken1,
+                new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+        TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+        transitions.onTransitionReady(transitToken1, info1, mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+        assertEquals(1, mDefaultHandler.activeCount());
+
+        transitions.runOnIdle(runnable2);
+        transitions.runOnIdle(runnable3);
+
+        // runnable2 and runnable3 aren't executed immediately because there is an active
+        // transaction.
+
+        IBinder transitToken2 = new Binder();
+        transitions.requestStartTransition(transitToken2,
+                new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
+        TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE)
+                .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+        transitions.onTransitionReady(transitToken2, info2, mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+        assertEquals(1, mDefaultHandler.activeCount());
+
+        mDefaultHandler.finishAll();
+        mMainExecutor.flushAll();
+        // first transition finished
+        verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any(), any());
+        verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any());
+        // But now the "queued" transition is running
+        assertEquals(1, mDefaultHandler.activeCount());
+
+        // runnable2 and runnable3 are still not executed because the second transition is still
+        // active.
+        verify(runnable2, times(0)).run();
+        verify(runnable3, times(0)).run();
+
+        mDefaultHandler.finishAll();
+        mMainExecutor.flushAll();
+        verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any(), any());
+
+        // runnable2 and runnable3 are executed after the second transition finishes because there
+        // are no other active transitions, runnable1 isn't executed again.
+        verify(runnable1, times(0)).run();
+        verify(runnable2, times(1)).run();
+        verify(runnable3, times(1)).run();
+
+        clearInvocations(runnable2);
+        clearInvocations(runnable3);
+
+        transitions.runOnIdle(runnable4);
+
+        // runnable4 is executed immediately because there are no active transitions, all other
+        // runnables aren't executed again.
+        verify(runnable1, times(0)).run();
+        verify(runnable2, times(0)).run();
+        verify(runnable3, times(0)).run();
+        verify(runnable4, times(1)).run();
+    }
+
     class TransitionInfoBuilder {
         final TransitionInfo mInfo;
 
@@ -749,7 +827,7 @@
         IWindowManager mockWM = mock(IWindowManager.class);
         final IDisplayWindowListener[] displayListener = new IDisplayWindowListener[1];
         try {
-            doReturn(new int[] {DEFAULT_DISPLAY}).when(mockWM).registerDisplayWindowListener(any());
+            doReturn(new int[]{DEFAULT_DISPLAY}).when(mockWM).registerDisplayWindowListener(any());
         } catch (RemoteException e) {
             // No remote stuff happening, so this can't be hit
         }
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index dd272cd..c24cabb 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -310,6 +310,11 @@
     return has101012Support;
 }
 
+bool HardwareBitmapUploader::hasAlpha8Support() {
+    static bool hasAlpha8Support = checkSupport(AHARDWAREBUFFER_FORMAT_R8_UNORM);
+    return hasAlpha8Support;
+}
+
 static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) {
     FormatInfo formatInfo;
     switch (skBitmap.info().colorType()) {
@@ -363,6 +368,13 @@
             }
             formatInfo.format = GL_RGBA;
             break;
+        case kAlpha_8_SkColorType:
+            formatInfo.isSupported = HardwareBitmapUploader::hasAlpha8Support();
+            formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8_UNORM;
+            formatInfo.format = GL_R8;
+            formatInfo.type = GL_UNSIGNED_BYTE;
+            formatInfo.vkFormat = VK_FORMAT_R8_UNORM;
+            break;
         default:
             ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
             formatInfo.valid = false;
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index 34f43cd..81057a2 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -30,11 +30,13 @@
 #ifdef __ANDROID__
     static bool hasFP16Support();
     static bool has1010102Support();
+    static bool hasAlpha8Support();
 #else
     static bool hasFP16Support() {
         return true;
     }
     static bool has1010102Support() { return true; }
+    static bool hasAlpha8Support() { return true; }
 #endif
 };
 
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 1a89cfd..67f4758 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -104,6 +104,10 @@
 
 sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) {
 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
+    if (bitmap.colorType() == kAlpha_8_SkColorType &&
+        !uirenderer::HardwareBitmapUploader::hasAlpha8Support()) {
+        return nullptr;
+    }
     return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
 #else
     return Bitmap::allocateHeapBitmap(bitmap.info());
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 8c98c72..2357dfe 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -133,6 +133,7 @@
 }
 
 void DrawFrameTask::postAndWait() {
+    ATRACE_CALL();
     AutoMutex _lock(mLock);
     mRenderThread->queue().post([this]() { run(); });
     mSignal.wait(mLock);
diff --git a/location/java/android/location/GnssExcessPathInfo.java b/location/java/android/location/GnssExcessPathInfo.java
new file mode 100644
index 0000000..72b2374
--- /dev/null
+++ b/location/java/android/location/GnssExcessPathInfo.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_ATTENUATION;
+import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH;
+import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH_UNC;
+import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_REFLECTING_PLANE;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Contains the info of an excess path signal caused by reflection
+ *
+ * @hide
+ */
+@SystemApi
+public final class GnssExcessPathInfo implements Parcelable {
+
+    private static final int HAS_EXCESS_PATH_LENGTH_MASK = EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH;
+    private static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK =
+            EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH_UNC;
+    private static final int HAS_REFLECTING_PLANE_MASK = EXCESS_PATH_INFO_HAS_REFLECTING_PLANE;
+    private static final int HAS_ATTENUATION_MASK = EXCESS_PATH_INFO_HAS_ATTENUATION;
+
+    /* A bitmask of fields present in this object (see HAS_* constants defined above) */
+    private final int mFlags;
+    private final float mExcessPathLengthMeters;
+    private final float mExcessPathLengthUncertaintyMeters;
+    @Nullable
+    private final GnssReflectingPlane mReflectingPlane;
+    private final float mAttenuationDb;
+
+    private GnssExcessPathInfo(
+            int flags,
+            float excessPathLengthMeters,
+            float excessPathLengthUncertaintyMeters,
+            @Nullable GnssReflectingPlane reflectingPlane,
+            float attenuationDb) {
+        mFlags = flags;
+        mExcessPathLengthMeters = excessPathLengthMeters;
+        mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
+        mReflectingPlane = reflectingPlane;
+        mAttenuationDb = attenuationDb;
+    }
+
+    /**
+     * Gets a bitmask of fields present in this object.
+     *
+     * <p>This API exists for JNI since it is easier for JNI to get one integer flag than looking up
+     * several has* methods.
+     * @hide
+     */
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */
+    public boolean hasExcessPathLength() {
+        return (mFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0;
+    }
+
+    /**
+     * Returns the excess path length to be subtracted from pseudorange before using it in
+     * calculating location.
+     *
+     * <p>{@link #hasExcessPathLength()} must be true when calling this method. Otherwise, an
+     * {@link UnsupportedOperationException} will be thrown.
+     */
+    @FloatRange(from = 0.0f)
+    public float getExcessPathLengthMeters() {
+        if (!hasExcessPathLength()) {
+            throw new UnsupportedOperationException(
+                    "getExcessPathLengthMeters() is not supported when hasExcessPathLength() is "
+                            + "false");
+        }
+        return mExcessPathLengthMeters;
+    }
+
+    /** Returns {@code true} if {@link #getExcessPathLengthUncertaintyMeters()} is valid. */
+    public boolean hasExcessPathLengthUncertainty() {
+        return (mFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0;
+    }
+
+    /**
+     * Returns the error estimate (1-sigma) for the excess path length estimate.
+     *
+     * <p>{@link #hasExcessPathLengthUncertainty()} must be true when calling this method.
+     * Otherwise, an {@link UnsupportedOperationException} will be thrown.
+     */
+    @FloatRange(from = 0.0f)
+    public float getExcessPathLengthUncertaintyMeters() {
+        if (!hasExcessPathLengthUncertainty()) {
+            throw new UnsupportedOperationException(
+                    "getExcessPathLengthUncertaintyMeters() is not supported when "
+                            + "hasExcessPathLengthUncertainty() is false");
+        }
+        return mExcessPathLengthUncertaintyMeters;
+    }
+
+    /**
+     * Returns {@code true} if {@link #getReflectingPlane()} is valid.
+     *
+     * <p>Returns false if the satellite signal goes through multiple reflections or if reflection
+     * plane serving is not supported.
+     */
+    public boolean hasReflectingPlane() {
+        return (mFlags & HAS_REFLECTING_PLANE_MASK) != 0;
+    }
+
+    /**
+     * Returns the reflecting plane characteristics at which the signal has bounced.
+     *
+     * <p>{@link #hasReflectingPlane()} must be true when calling this method. Otherwise, an
+     * {@link UnsupportedOperationException} will be thrown.
+     */
+    @NonNull
+    public GnssReflectingPlane getReflectingPlane() {
+        if (!hasReflectingPlane()) {
+            throw new UnsupportedOperationException(
+                    "getReflectingPlane() is not supported when hasReflectingPlane() is false");
+        }
+        return mReflectingPlane;
+    }
+
+    /** Returns {@code true} if {@link #getAttenuationDb()} is valid. */
+    public boolean hasAttenuation() {
+        return (mFlags & HAS_ATTENUATION_MASK) != 0;
+    }
+
+    /**
+     * Returns the expected reduction of signal strength of this path in non-negative dB.
+     *
+     * <p>{@link #hasAttenuation()} must be true when calling this method. Otherwise, an
+     * {@link UnsupportedOperationException} will be thrown.
+     */
+    @FloatRange(from = 0.0f)
+    public float getAttenuationDb() {
+        if (!hasAttenuation()) {
+            throw new UnsupportedOperationException(
+                    "getAttenuationDb() is not supported when hasAttenuation() is false");
+        }
+        return mAttenuationDb;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int parcelFlags) {
+        parcel.writeInt(mFlags);
+        if (hasExcessPathLength()) {
+            parcel.writeFloat(mExcessPathLengthMeters);
+        }
+        if (hasExcessPathLengthUncertainty()) {
+            parcel.writeFloat(mExcessPathLengthUncertaintyMeters);
+        }
+        if (hasReflectingPlane()) {
+            mReflectingPlane.writeToParcel(parcel, parcelFlags);
+        }
+        if (hasAttenuation()) {
+            parcel.writeFloat(mAttenuationDb);
+        }
+    }
+
+    public static final @NonNull Creator<GnssExcessPathInfo> CREATOR =
+            new Creator<GnssExcessPathInfo>() {
+                @Override
+                @NonNull
+                public GnssExcessPathInfo createFromParcel(@NonNull Parcel parcel) {
+                    int flags = parcel.readInt();
+                    float excessPathLengthMeters =
+                            (flags & HAS_EXCESS_PATH_LENGTH_MASK) != 0
+                                    ? parcel.readFloat() : 0;
+                    float excessPathLengthUncertaintyMeters =
+                            (flags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0
+                                    ? parcel.readFloat() : 0;
+                    GnssReflectingPlane reflectingPlane =
+                            (flags & HAS_REFLECTING_PLANE_MASK) != 0
+                                    ? GnssReflectingPlane.CREATOR.createFromParcel(parcel) : null;
+                    float attenuationDb =
+                            (flags & HAS_ATTENUATION_MASK) != 0
+                                    ? parcel.readFloat() : 0;
+                    return new GnssExcessPathInfo(flags, excessPathLengthMeters,
+                            excessPathLengthUncertaintyMeters, reflectingPlane, attenuationDb);
+                }
+
+                @Override
+                public GnssExcessPathInfo[] newArray(int i) {
+                    return new GnssExcessPathInfo[i];
+                }
+            };
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof GnssExcessPathInfo) {
+            GnssExcessPathInfo that = (GnssExcessPathInfo) obj;
+            return this.mFlags == that.mFlags
+                    && (!hasExcessPathLength() || Float.compare(this.mExcessPathLengthMeters,
+                    that.mExcessPathLengthMeters) == 0)
+                    && (!hasExcessPathLengthUncertainty() || Float.compare(
+                    this.mExcessPathLengthUncertaintyMeters,
+                    that.mExcessPathLengthUncertaintyMeters) == 0)
+                    && (!hasReflectingPlane() || Objects.equals(this.mReflectingPlane,
+                    that.mReflectingPlane))
+                    && (!hasAttenuation() || Float.compare(this.mAttenuationDb,
+                    that.mAttenuationDb) == 0);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mFlags,
+                mExcessPathLengthMeters,
+                mExcessPathLengthUncertaintyMeters,
+                mReflectingPlane,
+                mAttenuationDb);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("GnssExcessPathInfo[");
+        if (hasExcessPathLength()) {
+            builder.append(" ExcessPathLengthMeters=").append(mExcessPathLengthMeters);
+        }
+        if (hasExcessPathLengthUncertainty()) {
+            builder.append(" ExcessPathLengthUncertaintyMeters=").append(
+                    mExcessPathLengthUncertaintyMeters);
+        }
+        if (hasReflectingPlane()) {
+            builder.append(" ReflectingPlane=").append(mReflectingPlane);
+        }
+        if (hasAttenuation()) {
+            builder.append(" AttenuationDb=").append(mAttenuationDb);
+        }
+        builder.append(']');
+        return builder.toString();
+    }
+
+    /** Builder for {@link GnssExcessPathInfo}. */
+    public static final class Builder {
+        private int mFlags;
+        private float mExcessPathLengthMeters;
+        private float mExcessPathLengthUncertaintyMeters;
+        @Nullable
+        private GnssReflectingPlane mReflectingPlane;
+        private float mAttenuationDb;
+
+        /** Constructor for {@link Builder}. */
+        public Builder() {}
+
+        /**
+         * Sets the excess path length to be subtracted from pseudorange before using it in
+         * calculating location.
+         */
+        @NonNull
+        public Builder setExcessPathLengthMeters(
+                @FloatRange(from = 0.0f) float excessPathLengthMeters) {
+            Preconditions.checkArgumentInRange(excessPathLengthMeters, 0, Float.MAX_VALUE,
+                    "excessPathLengthMeters");
+            mExcessPathLengthMeters = excessPathLengthMeters;
+            mFlags |= HAS_EXCESS_PATH_LENGTH_MASK;
+            return this;
+        }
+
+        /**
+         * Clears the excess path length.
+         *
+         * <p>This is to negate {@link #setExcessPathLengthMeters} call.
+         */
+        @NonNull
+        public Builder clearExcessPathLengthMeters() {
+            mExcessPathLengthMeters = 0;
+            mFlags &= ~HAS_EXCESS_PATH_LENGTH_MASK;
+            return this;
+        }
+
+        /** Sets the error estimate (1-sigma) for the excess path length estimate */
+        @NonNull
+        public Builder setExcessPathLengthUncertaintyMeters(
+                @FloatRange(from = 0.0f) float excessPathLengthUncertaintyMeters) {
+            Preconditions.checkArgumentInRange(excessPathLengthUncertaintyMeters, 0,
+                    Float.MAX_VALUE, "excessPathLengthUncertaintyMeters");
+            mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
+            mFlags |= HAS_EXCESS_PATH_LENGTH_UNC_MASK;
+            return this;
+        }
+
+        /**
+         * Clears the error estimate (1-sigma) for the excess path length estimate
+         *
+         * <p>This is to negate {@link #setExcessPathLengthUncertaintyMeters} call.
+         */
+        @NonNull
+        public Builder clearExcessPathLengthUncertaintyMeters() {
+            mExcessPathLengthUncertaintyMeters = 0;
+            mFlags &= ~HAS_EXCESS_PATH_LENGTH_UNC_MASK;
+            return this;
+        }
+
+        /** Sets the reflecting plane information */
+        @NonNull
+        public Builder setReflectingPlane(@Nullable GnssReflectingPlane reflectingPlane) {
+            mReflectingPlane = reflectingPlane;
+            if (reflectingPlane != null) {
+                mFlags |= HAS_REFLECTING_PLANE_MASK;
+            } else {
+                mFlags &= ~HAS_REFLECTING_PLANE_MASK;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the attenuation value in dB.
+         */
+        @NonNull
+        public Builder setAttenuationDb(@FloatRange(from = 0.0f) float attenuationDb) {
+            Preconditions.checkArgumentInRange(attenuationDb, 0, Float.MAX_VALUE,
+                    "attenuationDb");
+            mAttenuationDb = attenuationDb;
+            mFlags |= HAS_ATTENUATION_MASK;
+            return this;
+        }
+
+        /**
+         * Clears the attenuation value in dB.
+         *
+         * <p>This is to negate {@link #setAttenuationDb(float)} call.
+         */
+        @NonNull
+        public Builder clearAttenuationDb() {
+            mAttenuationDb = 0;
+            mFlags &= ~HAS_ATTENUATION_MASK;
+            return this;
+        }
+
+        /** Builds a {@link GnssExcessPathInfo} instance as specified by this builder. */
+        @NonNull
+        public GnssExcessPathInfo build() {
+            return new GnssExcessPathInfo(
+                    mFlags,
+                    mExcessPathLengthMeters,
+                    mExcessPathLengthUncertaintyMeters,
+                    mReflectingPlane,
+                    mAttenuationDb);
+        }
+    }
+}
diff --git a/location/java/android/location/GnssReflectingPlane.java b/location/java/android/location/GnssReflectingPlane.java
index 1acdd1e..115cbec 100644
--- a/location/java/android/location/GnssReflectingPlane.java
+++ b/location/java/android/location/GnssReflectingPlane.java
@@ -22,9 +22,14 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * Holds the characteristics of the reflecting plane that a satellite signal has bounced from.
  *
+ * <p>Starting with Android T, this class supports {@link #equals} and {@link #hashCode}, which
+ * are not supported before that.
+ *
  * @hide
  */
 @SystemApi
@@ -107,18 +112,6 @@
                 }
             };
 
-    @NonNull
-    @Override
-    public String toString() {
-        final String format = "   %-29s = %s\n";
-        StringBuilder builder = new StringBuilder("ReflectingPlane:\n");
-        builder.append(String.format(format, "LatitudeDegrees = ", mLatitudeDegrees));
-        builder.append(String.format(format, "LongitudeDegrees = ", mLongitudeDegrees));
-        builder.append(String.format(format, "AltitudeMeters = ", mAltitudeMeters));
-        builder.append(String.format(format, "AzimuthDegrees = ", mAzimuthDegrees));
-        return builder.toString();
-    }
-
     @Override
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeDouble(mLatitudeDegrees);
@@ -127,6 +120,35 @@
         parcel.writeDouble(mAzimuthDegrees);
     }
 
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("ReflectingPlane[");
+        builder.append(" LatitudeDegrees=").append(mLatitudeDegrees);
+        builder.append(" LongitudeDegrees=").append(mLongitudeDegrees);
+        builder.append(" AltitudeMeters=").append(mAltitudeMeters);
+        builder.append(" AzimuthDegrees=").append(mAzimuthDegrees);
+        builder.append(']');
+        return builder.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof GnssReflectingPlane) {
+            GnssReflectingPlane that = (GnssReflectingPlane) obj;
+            return Double.compare(this.mLatitudeDegrees, that.mLatitudeDegrees) == 0
+                    && Double.compare(this.mLongitudeDegrees, that.mLongitudeDegrees) == 0
+                    && Double.compare(this.mAltitudeMeters, that.mAltitudeMeters) == 0
+                    && Double.compare(this.mAzimuthDegrees, that.mAzimuthDegrees) == 0;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLatitudeDegrees, mLatitudeDegrees, mAltitudeMeters, mAzimuthDegrees);
+    }
+
     /** Builder for {@link GnssReflectingPlane} */
     public static final class Builder {
         /** For documentation, see corresponding fields in {@link GnssReflectingPlane}. */
diff --git a/location/java/android/location/GnssSingleSatCorrection.java b/location/java/android/location/GnssSingleSatCorrection.java
index 262630b..a7fce0a 100644
--- a/location/java/android/location/GnssSingleSatCorrection.java
+++ b/location/java/android/location/GnssSingleSatCorrection.java
@@ -16,6 +16,11 @@
 
 package android.location;
 
+import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.SINGLE_SAT_CORRECTION_HAS_COMBINED_ATTENUATION;
+import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.SINGLE_SAT_CORRECTION_HAS_COMBINED_EXCESS_PATH_LENGTH;
+import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.SINGLE_SAT_CORRECTION_HAS_COMBINED_EXCESS_PATH_LENGTH_UNC;
+import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.SINGLE_SAT_CORRECTION_HAS_SAT_IS_LOS_PROBABILITY;
+
 import android.annotation.FloatRange;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -26,6 +31,8 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -36,106 +43,47 @@
 @SystemApi
 public final class GnssSingleSatCorrection implements Parcelable {
 
-    /**
-     * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
-     * #mProbSatIsLos}.
-     *
-     * @hide
-     */
-    public static final int HAS_PROB_SAT_IS_LOS_MASK = 1 << 0;
+    private static final int HAS_PROB_SAT_IS_LOS_MASK =
+            SINGLE_SAT_CORRECTION_HAS_SAT_IS_LOS_PROBABILITY;
+    private static final int HAS_COMBINED_EXCESS_PATH_LENGTH_MASK =
+            SINGLE_SAT_CORRECTION_HAS_COMBINED_EXCESS_PATH_LENGTH;
+    private static final int HAS_COMBINED_EXCESS_PATH_LENGTH_UNC_MASK =
+            SINGLE_SAT_CORRECTION_HAS_COMBINED_EXCESS_PATH_LENGTH_UNC;
+    private static final int HAS_COMBINED_ATTENUATION_MASK =
+            SINGLE_SAT_CORRECTION_HAS_COMBINED_ATTENUATION;
 
-    /**
-     * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
-     * #mExcessPathLengthMeters}.
-     *
-     * @hide
-     */
-    public static final int HAS_EXCESS_PATH_LENGTH_MASK = 1 << 1;
-
-    /**
-     * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
-     * #mExcessPathLengthUncertaintyMeters}.
-     *
-     * @hide
-     */
-    public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 1 << 2;
-
-    /**
-     * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
-     * #mReflectingPlane}.
-     *
-     * @hide
-     */
-    public static final int HAS_REFLECTING_PLANE_MASK = 1 << 3;
-
-    /** A bitmask of fields present in this object (see HAS_* constants defined above) */
+    /* A bitmask of fields present in this object (see HAS_* constants defined above). */
     private final int mSingleSatCorrectionFlags;
 
-    /** Defines the constellation of the given satellite as defined in {@link GnssStatus}. */
-    @GnssStatus.ConstellationType
     private final int mConstellationType;
-
-    /**
-     * Satellite vehicle ID number
-     *
-     * <p>Interpretation depends on {@link GnssStatus#getSvid(int)}.
-     */
-    @IntRange(from = 0)
     private final int mSatId;
-
-    /**
-     * Carrier frequency of the signal to be corrected, for example it can be the GPS center
-     * frequency for L1 = 1,575,420,000 Hz, varying GLO channels, etc.
-     *
-     * <p>For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two correction
-     * objects will be reported for this same satellite, in one of the correction objects, all the
-     * values related to L1 will be filled, and in the other all of the values related to L5 will be
-     * filled.
-     */
-    @FloatRange(from = 0.0f,  fromInclusive = false)
     private final float mCarrierFrequencyHz;
-
-    /**
-     * The probability that the satellite is estimated to be in Line-of-Sight condition at the given
-     * location.
-     */
-    @FloatRange(from = 0.0f, to = 1.0f)
     private final float mProbSatIsLos;
+    private final float mCombinedExcessPathLengthMeters;
+    private final float mCombinedExcessPathLengthUncertaintyMeters;
+    private final float mCombinedAttenuationDb;
 
-    /**
-     * Excess path length to be subtracted from pseudorange before using it in calculating location.
-     */
-    @FloatRange(from = 0.0f)
-    private final float mExcessPathLengthMeters;
-
-    /** Error estimate (1-sigma) for the Excess path length estimate */
-    @FloatRange(from = 0.0f)
-    private final float mExcessPathLengthUncertaintyMeters;
-
-    /**
-     * Defines the reflecting plane location and azimuth information
-     *
-     * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite
-     * signal goes through multiple reflections or if reflection plane serving is not supported.
-     */
-    @Nullable
-    private final GnssReflectingPlane mReflectingPlane;
+    @NonNull
+    private final List<GnssExcessPathInfo> mGnssExcessPathInfoList;
 
     private GnssSingleSatCorrection(int singleSatCorrectionFlags, int constellationType, int satId,
             float carrierFrequencyHz, float probSatIsLos, float excessPathLengthMeters,
-            float excessPathLengthUncertaintyMeters, GnssReflectingPlane reflectingPlane) {
+            float excessPathLengthUncertaintyMeters,
+            float combinedAttenuationDb,
+            @NonNull List<GnssExcessPathInfo> gnssExcessPathInfoList) {
         mSingleSatCorrectionFlags = singleSatCorrectionFlags;
         mConstellationType = constellationType;
         mSatId = satId;
         mCarrierFrequencyHz = carrierFrequencyHz;
         mProbSatIsLos = probSatIsLos;
-        mExcessPathLengthMeters = excessPathLengthMeters;
-        mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
-        mReflectingPlane = reflectingPlane;
+        mCombinedExcessPathLengthMeters = excessPathLengthMeters;
+        mCombinedExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
+        mCombinedAttenuationDb = combinedAttenuationDb;
+        mGnssExcessPathInfoList = gnssExcessPathInfoList;
     }
 
     /**
-     * Gets a bitmask of fields present in this object
+     * Gets a bitmask of fields present in this object.
      *
      * @hide
      */
@@ -193,29 +141,46 @@
     }
 
     /**
-     * Returns the Excess path length to be subtracted from pseudorange before using it in
+     * Returns the combined excess path length to be subtracted from pseudorange before using it in
      * calculating location.
      */
     @FloatRange(from = 0.0f)
     public float getExcessPathLengthMeters() {
-        return mExcessPathLengthMeters;
+        return mCombinedExcessPathLengthMeters;
     }
 
-    /** Returns the error estimate (1-sigma) for the Excess path length estimate */
+    /** Returns the error estimate (1-sigma) for the combined excess path length estimate. */
     @FloatRange(from = 0.0f)
     public float getExcessPathLengthUncertaintyMeters() {
-        return mExcessPathLengthUncertaintyMeters;
+        return mCombinedExcessPathLengthUncertaintyMeters;
     }
 
     /**
-     * Returns the reflecting plane characteristics at which the signal has bounced
+     * Returns the combined expected reduction of signal strength for this satellite in
+     * non-negative dB.
+     */
+    @FloatRange(from = 0.0f)
+    public float getCombinedAttenuationDb() {
+        return mCombinedAttenuationDb;
+    }
+
+    /**
+     * Returns the reflecting plane characteristics at which the signal has bounced.
      *
-     * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite
-     * signal goes through multiple reflections or if reflection plane serving is not supported
+     * @deprecated Combined excess path does not have a reflecting plane.
      */
     @Nullable
+    @Deprecated
     public GnssReflectingPlane getReflectingPlane() {
-        return mReflectingPlane;
+        return null;
+    }
+
+    /**
+     * Returns the list of {@link GnssExcessPathInfo} associated with this satellite signal.
+     */
+    @NonNull
+    public List<GnssExcessPathInfo> getGnssExcessPathInfoList() {
+        return mGnssExcessPathInfoList;
     }
 
     /** Returns {@code true} if {@link #getProbabilityLineOfSight()} is valid. */
@@ -225,17 +190,27 @@
 
     /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */
     public boolean hasExcessPathLength() {
-        return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0;
+        return (mSingleSatCorrectionFlags & HAS_COMBINED_EXCESS_PATH_LENGTH_MASK) != 0;
     }
 
     /** Returns {@code true} if {@link #getExcessPathLengthUncertaintyMeters()} is valid. */
     public boolean hasExcessPathLengthUncertainty() {
-        return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0;
+        return (mSingleSatCorrectionFlags & HAS_COMBINED_EXCESS_PATH_LENGTH_UNC_MASK) != 0;
     }
 
-    /** Returns {@code true} if {@link #getReflectingPlane()} is valid. */
+    /**
+     * Returns {@code true} if {@link #getReflectingPlane()} is valid.
+     *
+     * @deprecated Combined excess path does not have a reflecting plane.
+     */
+    @Deprecated
     public boolean hasReflectingPlane() {
-        return (mSingleSatCorrectionFlags & HAS_REFLECTING_PLANE_MASK) != 0;
+        return false;
+    }
+
+    /** Returns {@code true} if {@link #getCombinedAttenuationDb()} is valid. */
+    public boolean hasCombinedAttenuation() {
+        return (mSingleSatCorrectionFlags & HAS_COMBINED_ATTENUATION_MASK) != 0;
     }
 
     @Override
@@ -253,14 +228,15 @@
             parcel.writeFloat(mProbSatIsLos);
         }
         if (hasExcessPathLength()) {
-            parcel.writeFloat(mExcessPathLengthMeters);
+            parcel.writeFloat(mCombinedExcessPathLengthMeters);
         }
         if (hasExcessPathLengthUncertainty()) {
-            parcel.writeFloat(mExcessPathLengthUncertaintyMeters);
+            parcel.writeFloat(mCombinedExcessPathLengthUncertaintyMeters);
         }
-        if (hasReflectingPlane()) {
-            mReflectingPlane.writeToParcel(parcel, flags);
+        if (hasCombinedAttenuation()) {
+            parcel.writeFloat(mCombinedAttenuationDb);
         }
+        parcel.writeTypedList(mGnssExcessPathInfoList);
     }
 
     public static final Creator<GnssSingleSatCorrection> CREATOR =
@@ -274,18 +250,21 @@
                     float carrierFrequencyHz = parcel.readFloat();
                     float probSatIsLos = (singleSatCorrectionFlags & HAS_PROB_SAT_IS_LOS_MASK) != 0
                             ? parcel.readFloat() : 0;
-                    float excessPathLengthMeters =
-                            (singleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0
+                    float combinedExcessPathLengthMeters =
+                            (singleSatCorrectionFlags & HAS_COMBINED_EXCESS_PATH_LENGTH_MASK) != 0
                                     ? parcel.readFloat() : 0;
-                    float excessPathLengthUncertaintyMeters =
-                            (singleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0
+                    float combinedExcessPathLengthUncertaintyMeters =
+                            (singleSatCorrectionFlags & HAS_COMBINED_EXCESS_PATH_LENGTH_UNC_MASK)
+                                    != 0 ? parcel.readFloat() : 0;
+                    float combinedAttenuationDb =
+                            (singleSatCorrectionFlags & HAS_COMBINED_ATTENUATION_MASK) != 0
                                     ? parcel.readFloat() : 0;
-                    GnssReflectingPlane reflectingPlane =
-                            (singleSatCorrectionFlags & HAS_REFLECTING_PLANE_MASK) != 0
-                                    ? GnssReflectingPlane.CREATOR.createFromParcel(parcel) : null;
+                    List<GnssExcessPathInfo> gnssExcessPathInfoList = parcel.createTypedArrayList(
+                            GnssExcessPathInfo.CREATOR);
                     return new GnssSingleSatCorrection(singleSatCorrectionFlags, constellationType,
-                            satId, carrierFrequencyHz, probSatIsLos, excessPathLengthMeters,
-                            excessPathLengthUncertaintyMeters, reflectingPlane);
+                            satId, carrierFrequencyHz, probSatIsLos, combinedExcessPathLengthMeters,
+                            combinedExcessPathLengthUncertaintyMeters, combinedAttenuationDb,
+                            gnssExcessPathInfoList);
                 }
 
                 @Override
@@ -296,56 +275,24 @@
 
     @Override
     public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
+        if (obj instanceof GnssSingleSatCorrection) {
+            GnssSingleSatCorrection that = (GnssSingleSatCorrection) obj;
+            return this.mSingleSatCorrectionFlags == that.mSingleSatCorrectionFlags
+                    && this.mConstellationType == that.mConstellationType
+                    && this.mSatId == that.mSatId
+                    && Float.compare(mCarrierFrequencyHz, that.mCarrierFrequencyHz) == 0
+                    && (!hasValidSatelliteLineOfSight() || Float.compare(mProbSatIsLos,
+                    that.mProbSatIsLos) == 0)
+                    && (!hasExcessPathLength() || Float.compare(mCombinedExcessPathLengthMeters,
+                    that.mCombinedExcessPathLengthMeters) == 0)
+                    && (!hasExcessPathLengthUncertainty() || Float.compare(
+                    mCombinedExcessPathLengthUncertaintyMeters,
+                    that.mCombinedExcessPathLengthUncertaintyMeters) == 0)
+                    && (!hasCombinedAttenuation() || Float.compare(mCombinedAttenuationDb,
+                    that.mCombinedAttenuationDb) == 0)
+                    && mGnssExcessPathInfoList.equals(that.mGnssExcessPathInfoList);
         }
-        if (!(obj instanceof GnssSingleSatCorrection)) {
-            return false;
-        }
-
-        GnssSingleSatCorrection other = (GnssSingleSatCorrection) obj;
-        if (mConstellationType != other.mConstellationType) {
-            return false;
-        }
-        if (mSatId != other.mSatId) {
-            return false;
-        }
-        if (Float.compare(mCarrierFrequencyHz, other.mCarrierFrequencyHz) != 0) {
-            return false;
-        }
-
-        if (hasValidSatelliteLineOfSight() != other.hasValidSatelliteLineOfSight()) {
-            return false;
-        }
-        if (hasValidSatelliteLineOfSight()
-                && Float.compare(mProbSatIsLos, other.mProbSatIsLos) != 0) {
-            return false;
-        }
-
-        if (hasExcessPathLength() != other.hasExcessPathLength()) {
-            return false;
-        }
-        if (hasExcessPathLength()
-                && Float.compare(mExcessPathLengthMeters, other.mExcessPathLengthMeters) != 0) {
-            return false;
-        }
-
-        if (hasExcessPathLengthUncertainty() != other.hasExcessPathLengthUncertainty()) {
-            return false;
-        }
-        if (hasExcessPathLengthUncertainty() && Float.compare(mExcessPathLengthUncertaintyMeters,
-                other.mExcessPathLengthUncertaintyMeters) != 0) {
-            return false;
-        }
-
-        if (hasReflectingPlane() != other.hasReflectingPlane()) {
-            return false;
-        }
-        if (hasReflectingPlane()
-                && !mReflectingPlane.equals(other.mReflectingPlane)) {
-            return false;
-        }
-        return true;
+        return false;
     }
 
     @Override
@@ -355,9 +302,10 @@
                 mSatId,
                 mCarrierFrequencyHz,
                 mProbSatIsLos,
-                mExcessPathLengthMeters,
-                mExcessPathLengthUncertaintyMeters,
-                mReflectingPlane);
+                mCombinedExcessPathLengthMeters,
+                mCombinedExcessPathLengthUncertaintyMeters,
+                mCombinedAttenuationDb,
+                mGnssExcessPathInfoList);
     }
 
     @NonNull
@@ -371,14 +319,19 @@
             builder.append(" ProbSatIsLos=").append(mProbSatIsLos);
         }
         if (hasExcessPathLength()) {
-            builder.append(" ExcessPathLengthMeters=").append(mExcessPathLengthMeters);
+            builder.append(" CombinedExcessPathLengthMeters=").append(
+                    mCombinedExcessPathLengthMeters);
         }
         if (hasExcessPathLengthUncertainty()) {
-            builder.append(" ExcessPathLengthUncertaintyMeters=").append(
-                    mExcessPathLengthUncertaintyMeters);
+            builder.append(" CombinedExcessPathLengthUncertaintyMeters=").append(
+                    mCombinedExcessPathLengthUncertaintyMeters);
         }
-        if (hasReflectingPlane()) {
-            builder.append(" ReflectingPlane=").append(mReflectingPlane);
+        if (hasCombinedAttenuation()) {
+            builder.append(" CombinedAttenuationDb=").append(
+                    mCombinedAttenuationDb);
+        }
+        if (!mGnssExcessPathInfoList.isEmpty()) {
+            builder.append(' ').append(mGnssExcessPathInfoList.toString());
         }
         builder.append(']');
         return builder.toString();
@@ -386,21 +339,16 @@
 
     /** Builder for {@link GnssSingleSatCorrection} */
     public static final class Builder {
-
-        /**
-         * For documentation of below fields, see corresponding fields in {@link
-         * GnssSingleSatCorrection}.
-         */
         private int mSingleSatCorrectionFlags;
-
         private int mConstellationType;
         private int mSatId;
         private float mCarrierFrequencyHz;
         private float mProbSatIsLos;
-        private float mExcessPathLengthMeters;
-        private float mExcessPathLengthUncertaintyMeters;
-        @Nullable
-        private GnssReflectingPlane mReflectingPlane;
+        private float mCombinedExcessPathLengthMeters;
+        private float mCombinedExcessPathLengthUncertaintyMeters;
+        private float mCombinedAttenuationDb;
+        @NonNull
+        private List<GnssExcessPathInfo> mGnssExcessInfoList = new ArrayList<>();
 
         /** Sets the constellation type. */
         @NonNull public Builder setConstellationType(
@@ -409,18 +357,18 @@
             return this;
         }
 
-        /** Sets the Satellite ID defined in the ICD of the given constellation. */
+        /** Sets the satellite ID defined in the ICD of the given constellation. */
         @NonNull public Builder setSatelliteId(@IntRange(from = 0) int satId) {
             Preconditions.checkArgumentNonnegative(satId, "satId should be non-negative.");
             mSatId = satId;
             return this;
         }
 
-        /** Sets the Carrier frequency in Hz. */
+        /** Sets the carrier frequency in Hz. */
         @NonNull public Builder setCarrierFrequencyHz(
                 @FloatRange(from = 0.0f,  fromInclusive = false) float carrierFrequencyHz) {
-            Preconditions.checkArgument(
-                    carrierFrequencyHz >= 0, "carrierFrequencyHz should be non-negative.");
+            Preconditions.checkArgumentInRange(
+                    carrierFrequencyHz, 0, Float.MAX_VALUE, "carrierFrequencyHz");
             mCarrierFrequencyHz = carrierFrequencyHz;
             return this;
         }
@@ -450,58 +398,90 @@
         }
 
         /**
-         * Sets the Excess path length to be subtracted from pseudorange before using it in
+         * Sets the combined excess path length to be subtracted from pseudorange before using it in
          * calculating location.
          */
-        @NonNull public Builder setExcessPathLengthMeters(
-                @FloatRange(from = 0.0f) float excessPathLengthMeters) {
-            Preconditions.checkArgument(excessPathLengthMeters >= 0,
-                    "excessPathLengthMeters should be non-negative.");
-            mExcessPathLengthMeters = excessPathLengthMeters;
-            mSingleSatCorrectionFlags |= HAS_EXCESS_PATH_LENGTH_MASK;
+        @NonNull
+        public Builder setExcessPathLengthMeters(
+                @FloatRange(from = 0.0f) float combinedExcessPathLengthMeters) {
+            Preconditions.checkArgumentInRange(combinedExcessPathLengthMeters, 0, Float.MAX_VALUE,
+                    "excessPathLengthMeters");
+            mCombinedExcessPathLengthMeters = combinedExcessPathLengthMeters;
+            mSingleSatCorrectionFlags |= HAS_COMBINED_EXCESS_PATH_LENGTH_MASK;
             return this;
         }
 
         /**
-         * Clears the Excess path length.
+         * Clears the combined excess path length.
          *
          * <p>This is to negate {@link #setExcessPathLengthMeters} call.
          */
         @NonNull public Builder clearExcessPathLengthMeters() {
-            mExcessPathLengthMeters = 0;
-            mSingleSatCorrectionFlags &= ~HAS_EXCESS_PATH_LENGTH_MASK;
+            mCombinedExcessPathLengthMeters = 0;
+            mSingleSatCorrectionFlags &= ~HAS_COMBINED_EXCESS_PATH_LENGTH_MASK;
             return this;
         }
 
-        /** Sets the error estimate (1-sigma) for the Excess path length estimate */
+        /** Sets the error estimate (1-sigma) for the combined excess path length estimate. */
         @NonNull public Builder setExcessPathLengthUncertaintyMeters(
-                @FloatRange(from = 0.0f) float excessPathLengthUncertaintyMeters) {
-            Preconditions.checkArgument(excessPathLengthUncertaintyMeters >= 0,
-                    "excessPathLengthUncertaintyMeters should be non-negative.");
-            mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
-            mSingleSatCorrectionFlags |= HAS_EXCESS_PATH_LENGTH_UNC_MASK;
+                @FloatRange(from = 0.0f) float combinedExcessPathLengthUncertaintyMeters) {
+            Preconditions.checkArgumentInRange(combinedExcessPathLengthUncertaintyMeters, 0,
+                    Float.MAX_VALUE, "excessPathLengthUncertaintyMeters");
+            mCombinedExcessPathLengthUncertaintyMeters = combinedExcessPathLengthUncertaintyMeters;
+            mSingleSatCorrectionFlags |= HAS_COMBINED_EXCESS_PATH_LENGTH_UNC_MASK;
             return this;
         }
 
         /**
-         * Clears the error estimate (1-sigma) for the Excess path length estimate
+         * Clears the error estimate (1-sigma) for the combined excess path length estimate.
          *
          * <p>This is to negate {@link #setExcessPathLengthUncertaintyMeters} call.
          */
         @NonNull public Builder clearExcessPathLengthUncertaintyMeters() {
-            mExcessPathLengthUncertaintyMeters = 0;
-            mSingleSatCorrectionFlags &= ~HAS_EXCESS_PATH_LENGTH_UNC_MASK;
+            mCombinedExcessPathLengthUncertaintyMeters = 0;
+            mSingleSatCorrectionFlags &= ~HAS_COMBINED_EXCESS_PATH_LENGTH_UNC_MASK;
             return this;
         }
 
-        /** Sets the reflecting plane information */
+        /**
+         * Sets the combined attenuation in Db.
+         */
+        @NonNull public Builder setCombinedAttenuationDb(
+                @FloatRange(from = 0.0f) float combinedAttenuationDb) {
+            Preconditions.checkArgumentInRange(combinedAttenuationDb, 0, Float.MAX_VALUE,
+                    "combinedAttenuationDb");
+            mCombinedAttenuationDb = combinedAttenuationDb;
+            mSingleSatCorrectionFlags |= HAS_COMBINED_ATTENUATION_MASK;
+            return this;
+        }
+
+        /**
+         * Clears the combined attenuation.
+         *
+         * <p>This is to negate {@link #setCombinedAttenuationDb} call.
+         */
+        @NonNull public Builder clearCombinedAttenuationDb() {
+            mCombinedAttenuationDb = 0;
+            mSingleSatCorrectionFlags &= ~HAS_COMBINED_ATTENUATION_MASK;
+            return this;
+        }
+
+        /**
+         * Sets the reflecting plane information.
+         *
+         * @deprecated Combined excess path does not have a reflecting plane.
+         */
+        @Deprecated
         @NonNull public Builder setReflectingPlane(@Nullable GnssReflectingPlane reflectingPlane) {
-            mReflectingPlane = reflectingPlane;
-            if (reflectingPlane != null) {
-                mSingleSatCorrectionFlags |= HAS_REFLECTING_PLANE_MASK;
-            } else {
-                mSingleSatCorrectionFlags &= ~HAS_REFLECTING_PLANE_MASK;
-            }
+            return this;
+        }
+
+        /**
+         * Sets the collection of {@link GnssExcessPathInfo}.
+         */
+        @NonNull
+        public Builder setGnssExcessPathInfoList(@NonNull List<GnssExcessPathInfo> infoList) {
+            mGnssExcessInfoList = new ArrayList<>(infoList);
             return this;
         }
 
@@ -512,9 +492,10 @@
                     mSatId,
                     mCarrierFrequencyHz,
                     mProbSatIsLos,
-                    mExcessPathLengthMeters,
-                    mExcessPathLengthUncertaintyMeters,
-                    mReflectingPlane);
+                    mCombinedExcessPathLengthMeters,
+                    mCombinedExcessPathLengthUncertaintyMeters,
+                    mCombinedAttenuationDb,
+                    mGnssExcessInfoList);
         }
     }
 }
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index f1605f1..033056c 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -132,31 +132,31 @@
     }
 
     /**
-     * Construct a new Location object that is copied from an existing one.
+     * Constructs a new location copied from the given location.
      */
-    public Location(@NonNull Location l) {
-        set(l);
+    public Location(@NonNull Location location) {
+        set(location);
     }
 
     /**
      * Turns this location into a copy of the given location.
      */
-    public void set(@NonNull Location l) {
-        mFieldsMask = l.mFieldsMask;
-        mProvider = l.mProvider;
-        mTimeMs = l.mTimeMs;
-        mElapsedRealtimeNs = l.mElapsedRealtimeNs;
-        mElapsedRealtimeUncertaintyNs = l.mElapsedRealtimeUncertaintyNs;
-        mLatitudeDegrees = l.mLatitudeDegrees;
-        mLongitudeDegrees = l.mLongitudeDegrees;
-        mHorizontalAccuracyMeters = l.mHorizontalAccuracyMeters;
-        mAltitudeMeters = l.mAltitudeMeters;
-        mAltitudeAccuracyMeters = l.mAltitudeAccuracyMeters;
-        mSpeedMetersPerSecond = l.mSpeedMetersPerSecond;
-        mSpeedAccuracyMetersPerSecond = l.mSpeedAccuracyMetersPerSecond;
-        mBearingDegrees = l.mBearingDegrees;
-        mBearingAccuracyDegrees = l.mBearingAccuracyDegrees;
-        mExtras = (l.mExtras == null) ? null : new Bundle(l.mExtras);
+    public void set(@NonNull Location location) {
+        mFieldsMask = location.mFieldsMask;
+        mProvider = location.mProvider;
+        mTimeMs = location.mTimeMs;
+        mElapsedRealtimeNs = location.mElapsedRealtimeNs;
+        mElapsedRealtimeUncertaintyNs = location.mElapsedRealtimeUncertaintyNs;
+        mLatitudeDegrees = location.mLatitudeDegrees;
+        mLongitudeDegrees = location.mLongitudeDegrees;
+        mHorizontalAccuracyMeters = location.mHorizontalAccuracyMeters;
+        mAltitudeMeters = location.mAltitudeMeters;
+        mAltitudeAccuracyMeters = location.mAltitudeAccuracyMeters;
+        mSpeedMetersPerSecond = location.mSpeedMetersPerSecond;
+        mSpeedAccuracyMetersPerSecond = location.mSpeedAccuracyMetersPerSecond;
+        mBearingDegrees = location.mBearingDegrees;
+        mBearingAccuracyDegrees = location.mBearingAccuracyDegrees;
+        mExtras = (location.mExtras == null) ? null : new Bundle(location.mExtras);
     }
 
     /**
@@ -182,14 +182,13 @@
     }
 
     /**
-     * Returns the approximate distance in meters between this
-     * location and the given location.  Distance is defined using
-     * the WGS84 ellipsoid.
+     * Returns the approximate distance in meters between this location and the given location.
+     * Distance is defined using the WGS84 ellipsoid.
      *
      * @param dest the destination location
      * @return the approximate distance in meters
      */
-    public @FloatRange float distanceTo(@NonNull Location dest) {
+    public @FloatRange(from = 0.0) float distanceTo(@NonNull Location dest) {
         BearingDistanceCache cache = sBearingDistanceCache.get();
         // See if we already have the result
         if (mLatitudeDegrees != cache.mLat1 || mLongitudeDegrees != cache.mLon1
@@ -201,11 +200,10 @@
     }
 
     /**
-     * Returns the approximate initial bearing in degrees East of true
-     * North when traveling along the shortest path between this
-     * location and the given location.  The shortest path is defined
-     * using the WGS84 ellipsoid.  Locations that are (nearly)
-     * antipodal may produce meaningless results.
+     * Returns the approximate initial bearing in degrees east of true north when traveling along
+     * the shortest path between this location and the given location. The shortest path is defined
+     * using the WGS84 ellipsoid. Locations that are (nearly) antipodal may produce meaningless
+     * results.
      *
      * @param dest the destination location
      * @return the initial bearing in degrees
@@ -254,7 +252,7 @@
      * not be used to order or compare locations. Prefer {@link #getElapsedRealtimeNanos} for that
      * purpose, as the elapsed realtime clock is guaranteed to be monotonic.
      *
-     * <p>On the other hand, this method may be useful for presenting a human readable time to the
+     * <p>On the other hand, this method may be useful for presenting a human-readable time to the
      * user, or as a heuristic for comparing location fixes across reboot or across devices.
      *
      * <p>All locations generated by the {@link LocationManager} are guaranteed to have this time
@@ -263,25 +261,24 @@
      *
      * @return the Unix epoch time of this location
      */
-    public @IntRange long getTime() {
+    public @IntRange(from = 0) long getTime() {
         return mTimeMs;
     }
 
     /**
      * Sets the Unix epoch time of this location fix, in milliseconds since the start of the Unix
-     * epoch (00:00:00 January 1, 1970 UTC).
+     * epoch (00:00:00 January 1 1970 UTC).
      *
      * @param timeMs the Unix epoch time of this location
-     * @see #getTime for more information about times / clocks
      */
-    public void setTime(@IntRange long timeMs) {
+    public void setTime(@IntRange(from = 0) long timeMs) {
         mTimeMs = timeMs;
     }
 
     /**
      * Return the time of this fix in nanoseconds of elapsed realtime since system boot.
      *
-     * <p>This value can be compared with {@link android.os.SystemClock#elapsedRealtimeNanos}, to
+     * <p>This value can be compared with {@link android.os.SystemClock#elapsedRealtimeNanos} to
      * reliably order or compare locations. This is reliable because elapsed realtime is guaranteed
      * to be monotonic and continues to increment even when the system is in deep sleep (unlike
      * {@link #getTime}). However, since elapsed realtime is with reference to system boot, it does
@@ -292,7 +289,7 @@
      *
      * @return elapsed realtime of this location in nanoseconds
      */
-    public @IntRange long getElapsedRealtimeNanos() {
+    public @IntRange(from = 0) long getElapsedRealtimeNanos() {
         return mElapsedRealtimeNs;
     }
 
@@ -302,7 +299,7 @@
      * @return elapsed realtime of this location in milliseconds
      * @see #getElapsedRealtimeNanos()
      */
-    public @IntRange long getElapsedRealtimeMillis() {
+    public @IntRange(from = 0) long getElapsedRealtimeMillis() {
         return NANOSECONDS.toMillis(mElapsedRealtimeNs);
     }
 
@@ -312,7 +309,7 @@
      *
      * @return age of this location in milliseconds
      */
-    public @IntRange long getElapsedRealtimeAgeMillis() {
+    public @IntRange(from = 0) long getElapsedRealtimeAgeMillis() {
         return getElapsedRealtimeAgeMillis(SystemClock.elapsedRealtime());
     }
 
@@ -323,7 +320,8 @@
      * @param referenceRealtimeMs reference realtime in milliseconds
      * @return age of this location in milliseconds
      */
-    public @IntRange long getElapsedRealtimeAgeMillis(@IntRange long referenceRealtimeMs) {
+    public long getElapsedRealtimeAgeMillis(
+            @IntRange(from = 0) long referenceRealtimeMs) {
         return referenceRealtimeMs - getElapsedRealtimeMillis();
     }
 
@@ -332,7 +330,7 @@
      *
      * @param elapsedRealtimeNs elapsed realtime in nanoseconds
      */
-    public void setElapsedRealtimeNanos(@IntRange long elapsedRealtimeNs) {
+    public void setElapsedRealtimeNanos(@IntRange(from = 0) long elapsedRealtimeNs) {
         mElapsedRealtimeNs = elapsedRealtimeNs;
     }
 
@@ -346,7 +344,7 @@
      *
      * @return uncertainty in nanoseconds of the elapsed realtime of this location
      */
-    public @FloatRange double getElapsedRealtimeUncertaintyNanos() {
+    public @FloatRange(from = 0.0) double getElapsedRealtimeUncertaintyNanos() {
         return mElapsedRealtimeUncertaintyNs;
     }
 
@@ -358,20 +356,20 @@
      *                                     this location
      */
     public void setElapsedRealtimeUncertaintyNanos(
-            @FloatRange double elapsedRealtimeUncertaintyNs) {
+            @FloatRange(from = 0.0) double elapsedRealtimeUncertaintyNs) {
         mElapsedRealtimeUncertaintyNs = elapsedRealtimeUncertaintyNs;
         mFieldsMask |= HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK;
     }
 
     /**
-     * True if this location has a elapsed realtime uncertainty, false otherwise.
+     * True if this location has an elapsed realtime uncertainty, false otherwise.
      */
     public boolean hasElapsedRealtimeUncertaintyNanos() {
         return (mFieldsMask & HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK) != 0;
     }
 
     /**
-     * Removes the elapsed realtime uncertainy from this location.
+     * Removes the elapsed realtime uncertainty from this location.
      */
     public void removeElapsedRealtimeUncertaintyNanos() {
         mFieldsMask &= ~HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK;
@@ -383,7 +381,7 @@
      *
      * @return latitude of this location
      */
-    public @FloatRange double getLatitude() {
+    public @FloatRange(from = -90.0, to = 90.0) double getLatitude() {
         return mLatitudeDegrees;
     }
 
@@ -392,7 +390,7 @@
      *
      * @param latitudeDegrees latitude in degrees
      */
-    public void setLatitude(@FloatRange double latitudeDegrees) {
+    public void setLatitude(@FloatRange(from = -90.0, to = 90.0) double latitudeDegrees) {
         mLatitudeDegrees = latitudeDegrees;
     }
 
@@ -402,7 +400,7 @@
      *
      * @return longitude of this location
      */
-    public @FloatRange double getLongitude() {
+    public @FloatRange(from = -180.0, to = 180.0) double getLongitude() {
         return mLongitudeDegrees;
     }
 
@@ -411,7 +409,7 @@
      *
      * @param longitudeDegrees longitude in degrees
      */
-    public void setLongitude(@FloatRange double longitudeDegrees) {
+    public void setLongitude(@FloatRange(from = -180.0, to = 180.0) double longitudeDegrees) {
         mLongitudeDegrees = longitudeDegrees;
     }
 
@@ -423,12 +421,12 @@
      * reported location, there is a 68% chance that the true location falls within this circle.
      * This accuracy value is only valid for horizontal positioning, and not vertical positioning.
      *
-     * <p>This is only valid if {@link #hasSpeed()} is true. All locations generated by the
+     * <p>This is only valid if {@link #hasAccuracy()} is true. All locations generated by the
      * {@link LocationManager} include horizontal accuracy.
      *
      * @return horizontal accuracy of this location
      */
-    public @FloatRange float getAccuracy() {
+    public @FloatRange(from = 0.0) float getAccuracy() {
         return mHorizontalAccuracyMeters;
     }
 
@@ -437,7 +435,7 @@
      *
      * @param horizontalAccuracyMeters horizontal altitude in meters
      */
-    public void setAccuracy(@FloatRange float horizontalAccuracyMeters) {
+    public void setAccuracy(@FloatRange(from = 0.0) float horizontalAccuracyMeters) {
         mHorizontalAccuracyMeters = horizontalAccuracyMeters;
         mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK;
     }
@@ -500,7 +498,7 @@
      *
      * @return vertical accuracy of this location
      */
-    public @FloatRange float getVerticalAccuracyMeters() {
+    public @FloatRange(from = 0.0) float getVerticalAccuracyMeters() {
         return mAltitudeAccuracyMeters;
     }
 
@@ -509,7 +507,7 @@
      *
      * @param altitudeAccuracyMeters altitude accuracy in meters
      */
-    public void setVerticalAccuracyMeters(@FloatRange float altitudeAccuracyMeters) {
+    public void setVerticalAccuracyMeters(@FloatRange(from = 0.0) float altitudeAccuracyMeters) {
         mAltitudeAccuracyMeters = altitudeAccuracyMeters;
         mFieldsMask |= HAS_ALTITUDE_ACCURACY_MASK;
     }
@@ -538,17 +536,16 @@
      *
      * @return speed at the time of this location
      */
-    public @FloatRange float getSpeed() {
+    public @FloatRange(from = 0.0) float getSpeed() {
         return mSpeedMetersPerSecond;
     }
 
     /**
-     * Set the speed at the time of this location, in meters per second. Prefer not to set negative
-     * speeds.
+     * Set the speed at the time of this location, in meters per second.
      *
      * @param speedMetersPerSecond speed in meters per second
      */
-    public void setSpeed(@FloatRange float speedMetersPerSecond) {
+    public void setSpeed(@FloatRange(from = 0.0) float speedMetersPerSecond) {
         mSpeedMetersPerSecond = speedMetersPerSecond;
         mFieldsMask |= HAS_SPEED_MASK;
     }
@@ -576,7 +573,7 @@
      *
      * @return vertical accuracy of this location
      */
-    public @FloatRange float getSpeedAccuracyMetersPerSecond() {
+    public @FloatRange(from = 0.0) float getSpeedAccuracyMetersPerSecond() {
         return mSpeedAccuracyMetersPerSecond;
     }
 
@@ -585,7 +582,8 @@
      *
      * @param speedAccuracyMeterPerSecond speed accuracy in meters per second
      */
-    public void setSpeedAccuracyMetersPerSecond(@FloatRange float speedAccuracyMeterPerSecond) {
+    public void setSpeedAccuracyMetersPerSecond(
+            @FloatRange(from = 0.0) float speedAccuracyMeterPerSecond) {
         mSpeedAccuracyMetersPerSecond = speedAccuracyMeterPerSecond;
         mFieldsMask |= HAS_SPEED_ACCURACY_MASK;
     }
@@ -613,7 +611,7 @@
      *
      * @return bearing at the time of this location
      */
-    public @FloatRange(from = 0f, to = 360f, toInclusive = false) float getBearing() {
+    public @FloatRange(from = 0.0, to = 360.0, toInclusive = false) float getBearing() {
         return mBearingDegrees;
     }
 
@@ -663,7 +661,7 @@
      *
      * @return bearing accuracy in degrees of this location
      */
-    public @FloatRange float getBearingAccuracyDegrees() {
+    public @FloatRange(from = 0.0) float getBearingAccuracyDegrees() {
         return mBearingAccuracyDegrees;
     }
 
@@ -672,7 +670,7 @@
      *
      * @param bearingAccuracyDegrees bearing accuracy in degrees
      */
-    public void setBearingAccuracyDegrees(@FloatRange float bearingAccuracyDegrees) {
+    public void setBearingAccuracyDegrees(@FloatRange(from = 0.0) float bearingAccuracyDegrees) {
         mBearingAccuracyDegrees = bearingAccuracyDegrees;
         mFieldsMask |= HAS_BEARING_ACCURACY_MASK;
     }
@@ -692,9 +690,11 @@
     }
 
     /**
-     * Returns true if the Location came from a mock provider.
+     * Returns true if this is a mock location. If this location comes from the Android framework,
+     * this indicates that the location was provided by a test location provider, and thus may not
+     * be related to the actual location of the device.
      *
-     * @return true if this Location came from a mock provider, false otherwise
+     * @return true if this location came from a mock provider, false otherwise
      * @deprecated Prefer {@link #isMock()} instead.
      */
     @Deprecated
@@ -703,9 +703,9 @@
     }
 
     /**
-     * Flag this Location as having come from a mock provider or not.
+     * Flag this location as having come from a mock provider or not.
      *
-     * @param isFromMockProvider true if this Location came from a mock provider, false otherwise
+     * @param isFromMockProvider true if this location came from a mock provider, false otherwise
      * @deprecated Prefer {@link #setMock(boolean)} instead.
      * @hide
      */
@@ -745,7 +745,7 @@
      * will be present for any location.
      *
      * <ul>
-     * <li> satellites - the number of satellites used to derive the GNSS fix
+     * <li> satellites - the number of satellites used to derive a GNSS fix
      * </ul>
      */
     public @Nullable Bundle getExtras() {
@@ -899,7 +899,13 @@
         return s.toString();
     }
 
-    /** Dumps location. */
+    /**
+     * Dumps location information to the given Printer.
+     *
+     * @deprecated Prefer to use {@link #toString()} along with whatever custom formatting is
+     * required instead of this method. It is not this class's job to manage print representations.
+     */
+    @Deprecated
     public void dump(@NonNull Printer pw, @Nullable String prefix) {
         pw.println(prefix + this);
     }
@@ -1209,10 +1215,10 @@
      * @throws IllegalArgumentException if results is null or has length < 1
      */
     public static void distanceBetween(
-            @FloatRange double startLatitude,
-            @FloatRange double startLongitude,
-            @FloatRange double endLatitude,
-            @FloatRange double endLongitude,
+            @FloatRange(from = -90.0, to = 90.0) double startLatitude,
+            @FloatRange(from = -180.0, to = 180.0) double startLongitude,
+            @FloatRange(from = -90.0, to = 90.0) double endLatitude,
+            @FloatRange(from = -180.0, to = 180.0)  double endLongitude,
             float[] results) {
         if (results == null || results.length < 1) {
             throw new IllegalArgumentException("results is null or has length < 1");
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index bdbb740..c186700 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -39,6 +39,7 @@
 import android.media.IRingtonePlayer;
 import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.ISpatializerCallback;
+import android.media.ISpatializerHeadTrackerAvailableCallback;
 import android.media.ISpatializerHeadTrackingModeCallback;
 import android.media.ISpatializerHeadToSoundStagePoseCallback;
 import android.media.ISpatializerOutputCallback;
@@ -414,6 +415,11 @@
 
     boolean isHeadTrackerEnabled(in AudioDeviceAttributes device);
 
+    boolean isHeadTrackerAvailable();
+
+    void registerSpatializerHeadTrackerAvailableCallback(
+            in ISpatializerHeadTrackerAvailableCallback cb, boolean register);
+
     void setSpatializerEnabled(boolean enabled);
 
     boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af);
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl b/media/java/android/media/ISpatializerHeadTrackerAvailableCallback.aidl
similarity index 62%
copy from media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
copy to media/java/android/media/ISpatializerHeadTrackerAvailableCallback.aidl
index 5e15016..dc5ee1d 100644
--- a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
+++ b/media/java/android/media/ISpatializerHeadTrackerAvailableCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,14 @@
  * limitations under the License.
  */
 
-package android.media.tv.interactive;
+package android.media;
 
-parcelable TvInteractiveAppInfo;
\ No newline at end of file
+/**
+ * AIDL for the AudioService to signal whether audio device used by Spatializer has head tracker.
+ *
+ * {@hide}
+ */
+oneway interface ISpatializerHeadTrackerAvailableCallback {
+
+    void dispatchSpatializerHeadTrackerAvailable(boolean available);
+}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 4956dbe..7d3f916 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -793,8 +793,11 @@
      * By default, the decoder will output the same number of channels as present in the encoded
      * stream, if supported. Set this value to limit the number of output channels, and use
      * the downmix information in the stream, if available.
-     * <p>Values larger than the number of channels in the content to decode are ignored.
+     * <p>Values larger than the number of channels in the content to decode behave just
+     * like the actual channel count of the content (e.g. passing 99 for the decoding of 5.1 content
+     * will behave like using 6).
      * <p>This key is only used during decoding.
+     * @deprecated Use the non-AAC-specific key {@link #KEY_MAX_OUTPUT_CHANNEL_COUNT} instead
      */
     public static final String KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT = "aac-max-output-channel_count";
 
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index be0ef37..74ca4b8 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -185,6 +185,45 @@
         return false;
     }
 
+    /**
+     * Returns whether a head tracker is currently available for the audio device used by the
+     * spatializer effect.
+     * @return true if a head tracker is available and the effect is enabled, false otherwise.
+     * @see OnHeadTrackerAvailableListener
+     * @see #addOnHeadTrackerAvailableListener(Executor, OnHeadTrackerAvailableListener)
+     */
+    public boolean isHeadTrackerAvailable() {
+        try {
+            return mAm.getService().isHeadTrackerAvailable();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
+     * Adds a listener to be notified of changes to the availability of a head tracker.
+     * @param executor the {@code Executor} handling the callback
+     * @param listener the listener to receive availability updates
+     * @see #removeOnHeadTrackerAvailableListener(OnHeadTrackerAvailableListener)
+     */
+    public void addOnHeadTrackerAvailableListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OnHeadTrackerAvailableListener listener) {
+        mHeadTrackerListenerMgr.addListener(executor, listener,
+                "addOnHeadTrackerAvailableListener",
+                () -> new SpatializerHeadTrackerAvailableDispatcherStub());
+    }
+
+    /**
+     * Removes a previously registered listener for the availability of a head tracker.
+     * @param listener the listener previously registered with
+     *      {@link #addOnHeadTrackerAvailableListener(Executor, OnHeadTrackerAvailableListener)}
+     */
+    public void removeOnHeadTrackerAvailableListener(
+            @NonNull OnHeadTrackerAvailableListener listener) {
+        mHeadTrackerListenerMgr.removeListener(listener, "removeOnHeadTrackerAvailableListener");
+    }
+
     /** @hide */
     @IntDef(flag = false, value = {
             SPATIALIZER_IMMERSIVE_LEVEL_OTHER,
@@ -401,6 +440,22 @@
                 @HeadTrackingModeSet int mode);
     }
 
+    /**
+     * Interface to be notified of changes to the availability of a head tracker on the audio
+     * device to be used by the spatializer effect.
+     */
+    public interface OnHeadTrackerAvailableListener {
+        /**
+         * Called when the availability of the head tracker changed.
+         * @param spatializer the {@code Spatializer} instance for which the head tracker
+         *                    availability was updated
+         * @param available true if the audio device that would output audio processed by
+         *                  the {@code Spatializer} has a head tracker associated with it, false
+         *                  otherwise.
+         */
+        void onHeadTrackerAvailableChanged(@NonNull Spatializer spatializer,
+                boolean available);
+    }
 
     /**
      * @hide
@@ -827,8 +882,12 @@
 
     /**
      * @hide
-     * Returns the id of the output stream used for the spatializer effect playback
+     * Returns the id of the output stream used for the spatializer effect playback.
+     * This getter or associated listener {@link OnSpatializerOutputChangedListener} can be used for
+     * handling spatializer output-specific configurations (e.g. disabling speaker post-processing
+     * to avoid double-processing of the spatialized path).
      * @return id of the output stream, or 0 if no spatializer playback is active
+     * @see #setOnSpatializerOutputChangedListener(Executor, OnSpatializerOutputChangedListener)
      */
     @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
     @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
@@ -865,6 +924,8 @@
             mOutputDispatcher = new SpatializerOutputDispatcherStub();
             try {
                 mAm.getService().registerSpatializerOutputCallback(mOutputDispatcher);
+                // immediately report the current output
+                mOutputDispatcher.dispatchSpatializerOutputChanged(getOutput());
             } catch (RemoteException e) {
                 mOutputListener = null;
                 mOutputDispatcher = null;
@@ -935,6 +996,36 @@
     }
 
     //-----------------------------------------------------------------------------
+    // head tracker availability callback management and stub
+    /**
+     * manages the OnHeadTrackerAvailableListener listeners and the
+     * SpatializerHeadTrackerAvailableDispatcherStub
+     */
+    private final CallbackUtil.LazyListenerManager<OnHeadTrackerAvailableListener>
+            mHeadTrackerListenerMgr = new CallbackUtil.LazyListenerManager();
+
+    private final class SpatializerHeadTrackerAvailableDispatcherStub
+            extends ISpatializerHeadTrackerAvailableCallback.Stub
+            implements CallbackUtil.DispatcherStub {
+        @Override
+        public void register(boolean register) {
+            try {
+                mAm.getService().registerSpatializerHeadTrackerAvailableCallback(this, register);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+
+        @Override
+        @SuppressLint("GuardedBy") // lock applied inside callListeners method
+        public void dispatchSpatializerHeadTrackerAvailable(boolean available) {
+            mHeadTrackerListenerMgr.callListeners(
+                    (listener) -> listener.onHeadTrackerAvailableChanged(
+                            Spatializer.this, available));
+        }
+    }
+
+    //-----------------------------------------------------------------------------
     // head pose callback management and stub
     private final Object mPoseListenerLock = new Object();
     /**
diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl
index 19fc052..b136d5b 100644
--- a/media/java/android/media/projection/IMediaProjection.aidl
+++ b/media/java/android/media/projection/IMediaProjection.aidl
@@ -17,6 +17,7 @@
 package android.media.projection;
 
 import android.media.projection.IMediaProjectionCallback;
+import android.window.WindowContainerToken;
 
 /** {@hide} */
 interface IMediaProjection {
@@ -28,4 +29,16 @@
     int applyVirtualDisplayFlags(int flags);
     void registerCallback(IMediaProjectionCallback callback);
     void unregisterCallback(IMediaProjectionCallback callback);
+
+    /**
+     * Returns the {@link android.window.WindowContainerToken} identifying the task to record, or
+     * {@code null} if there is none.
+     */
+    WindowContainerToken getTaskRecordingWindowContainerToken();
+
+    /**
+     * Updates the {@link android.window.WindowContainerToken} identifying the task to record, or
+     * {@code null} if there is none.
+     */
+    void setTaskRecordingWindowContainerToken(in WindowContainerToken token);
 }
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 4dde5e8..b5f9593 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -25,13 +25,14 @@
 import android.hardware.display.VirtualDisplay;
 import android.hardware.display.VirtualDisplayConfig;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.ContentRecordingSession;
+import android.view.IWindowManager;
 import android.view.Surface;
 import android.view.WindowManagerGlobal;
+import android.window.WindowContainerToken;
 
 import java.util.Map;
 
@@ -171,16 +172,34 @@
             @NonNull VirtualDisplayConfig.Builder virtualDisplayConfig,
             @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
         try {
-            final Context windowContext = mContext.createWindowContext(
-                    mContext.getDisplayNoVerify(),
-                    TYPE_APPLICATION, null /* options */);
-            final IBinder windowContextToken = windowContext.getWindowContextToken();
+            final IWindowManager wmService = WindowManagerGlobal.getWindowManagerService();
+            final WindowContainerToken taskWindowContainerToken =
+                    mImpl.getTaskRecordingWindowContainerToken();
+            Context windowContext = null;
+            ContentRecordingSession session;
+            if (taskWindowContainerToken == null) {
+                windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
+                        TYPE_APPLICATION, null /* options */);
+                session = ContentRecordingSession.createDisplaySession(
+                        windowContext.getWindowContextToken());
+            } else {
+                session = ContentRecordingSession.createTaskSession(
+                        taskWindowContainerToken.asBinder());
+            }
             virtualDisplayConfig.setWindowManagerMirroring(true);
             final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
             final VirtualDisplay virtualDisplay = dm.createVirtualDisplay(this,
-                    virtualDisplayConfig.build(),
-                    callback, handler, windowContext);
-            setSession(windowContextToken, virtualDisplay);
+                    virtualDisplayConfig.build(), callback, handler, windowContext);
+            if (virtualDisplay == null) {
+                // Since WM handling a new display and DM creating a new VirtualDisplay is async,
+                // WM may have tried to start task recording and encountered an error that required
+                // stopping recording entirely. The VirtualDisplay would then be null when the
+                // MediaProjection is no longer active.
+                return null;
+            }
+            session.setDisplayId(virtualDisplay.getDisplay().getDisplayId());
+            // Successfully set up, so save the current session details.
+            wmService.setContentRecordingSession(session);
             return virtualDisplay;
         } catch (RemoteException e) {
             // Can not capture if WMS is not accessible, so bail out.
@@ -189,28 +208,6 @@
     }
 
     /**
-     * Updates the {@link ContentRecordingSession} describing the recording taking place on this
-     * {@link VirtualDisplay}.
-     *
-     * @throws RemoteException if updating the session on the server failed.
-     */
-    private void setSession(@NonNull IBinder windowContextToken,
-            @Nullable VirtualDisplay virtualDisplay)
-            throws RemoteException {
-        if (virtualDisplay == null) {
-            // Not able to set up a new VirtualDisplay.
-            return;
-        }
-        // Identify the VirtualDisplay that will be hosting the recording.
-        ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
-                windowContextToken);
-        session.setDisplayId(virtualDisplay.getDisplay().getDisplayId());
-        // TODO(b/216625226) handle task recording.
-        // Successfully set up, so save the current session details.
-        WindowManagerGlobal.getWindowManagerService().setContentRecordingSession(session);
-    }
-
-    /**
      * Stops projection.
      */
     public void stop() {
diff --git a/media/java/android/media/tv/AitInfo.java b/media/java/android/media/tv/AitInfo.java
index 8e80a62..c88a2b5 100644
--- a/media/java/android/media/tv/AitInfo.java
+++ b/media/java/android/media/tv/AitInfo.java
@@ -17,7 +17,7 @@
 package android.media.tv;
 
 import android.annotation.NonNull;
-import android.media.tv.interactive.TvInteractiveAppInfo;
+import android.media.tv.interactive.TvInteractiveAppServiceInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -50,7 +50,7 @@
     /**
      * Constructs AIT info.
      */
-    public AitInfo(@TvInteractiveAppInfo.InteractiveAppType int type, int version) {
+    public AitInfo(@TvInteractiveAppServiceInfo.InteractiveAppType int type, int version) {
         mType = type;
         mVersion = version;
     }
@@ -58,7 +58,7 @@
     /**
      * Gets interactive app type.
      */
-    @TvInteractiveAppInfo.InteractiveAppType
+    @TvInteractiveAppServiceInfo.InteractiveAppType
     public int getType() {
         return mType;
     }
diff --git a/media/java/android/media/tv/CommandRequest.java b/media/java/android/media/tv/CommandRequest.java
index ffb6e07..3245fb5 100644
--- a/media/java/android/media/tv/CommandRequest.java
+++ b/media/java/android/media/tv/CommandRequest.java
@@ -24,6 +24,8 @@
  * A request for command from broadcast signal.
  */
 public final class CommandRequest extends BroadcastInfoRequest implements Parcelable {
+    public static final String ARGUMENT_TYPE_XML = "xml";
+    public static final String ARGUMENT_TYPE_JSON = "json";
     private static final @TvInputManager.BroadcastInfoType int REQUEST_TYPE =
             TvInputManager.BROADCAST_INFO_TYPE_COMMAND;
 
@@ -41,35 +43,38 @@
                 }
             };
 
-    private final String mNameSpace;
+    private final String mNamespace;
     private final String mName;
     private final String mArguments;
+    private final String mArgumentType;
 
     static CommandRequest createFromParcelBody(Parcel in) {
         return new CommandRequest(in);
     }
 
-    public CommandRequest(int requestId, @RequestOption int option, @NonNull String nameSpace,
-            @NonNull String name, @NonNull String arguments) {
+    public CommandRequest(int requestId, @RequestOption int option, @NonNull String namespace,
+            @NonNull String name, @NonNull String arguments, @NonNull String argumentType) {
         super(REQUEST_TYPE, requestId, option);
-        mNameSpace = nameSpace;
+        mNamespace = namespace;
         mName = name;
         mArguments = arguments;
+        mArgumentType = argumentType;
     }
 
     CommandRequest(Parcel source) {
         super(REQUEST_TYPE, source);
-        mNameSpace = source.readString();
+        mNamespace = source.readString();
         mName = source.readString();
         mArguments = source.readString();
+        mArgumentType = source.readString();
     }
 
     /**
      * Gets the namespace of the command.
      */
     @NonNull
-    public String getNameSpace() {
-        return mNameSpace;
+    public String getNamespace() {
+        return mNamespace;
     }
 
     /**
@@ -89,6 +94,15 @@
         return mArguments;
     }
 
+    /**
+     * Gets the argument type of the command.
+     * It could be either JSON or XML.
+     */
+    @NonNull
+    public String getArgumentType() {
+        return mArgumentType;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -97,8 +111,9 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
-        dest.writeString(mNameSpace);
+        dest.writeString(mNamespace);
         dest.writeString(mName);
         dest.writeString(mArguments);
+        dest.writeString(mArgumentType);
     }
 }
diff --git a/media/java/android/media/tv/CommandResponse.java b/media/java/android/media/tv/CommandResponse.java
index c8853f1..8e448cd 100644
--- a/media/java/android/media/tv/CommandResponse.java
+++ b/media/java/android/media/tv/CommandResponse.java
@@ -25,6 +25,8 @@
  * A response for command from broadcast signal.
  */
 public final class CommandResponse extends BroadcastInfoResponse implements Parcelable {
+    public static final String RESPONSE_TYPE_XML = "xml";
+    public static final String RESPONSE_TYPE_JSON = "json";
     private static final @TvInputManager.BroadcastInfoType int RESPONSE_TYPE =
             TvInputManager.BROADCAST_INFO_TYPE_COMMAND;
 
@@ -43,20 +45,23 @@
             };
 
     private final String mResponse;
+    private final String mResponseType;
 
     static CommandResponse createFromParcelBody(Parcel in) {
         return new CommandResponse(in);
     }
 
-    public CommandResponse(int requestId, int sequence,
-            @ResponseResult int responseResult, @Nullable String response) {
+    public CommandResponse(int requestId, int sequence, @ResponseResult int responseResult,
+            @Nullable String response, @NonNull String responseType) {
         super(RESPONSE_TYPE, requestId, sequence, responseResult);
         mResponse = response;
+        mResponseType = responseType;
     }
 
     CommandResponse(Parcel source) {
         super(RESPONSE_TYPE, source);
         mResponse = source.readString();
+        mResponseType = source.readString();
     }
 
     /**
@@ -68,6 +73,15 @@
         return mResponse;
     }
 
+    /**
+     * Gets the type of the command response.
+     * It could be either JSON or XML.
+     */
+    @NonNull
+    public String getResponseType() {
+        return mResponseType;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -77,5 +91,6 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeString(mResponse);
+        dest.writeString(mResponseType);
     }
 }
diff --git a/media/java/android/media/tv/SectionRequest.java b/media/java/android/media/tv/SectionRequest.java
index 5957528..078e832 100644
--- a/media/java/android/media/tv/SectionRequest.java
+++ b/media/java/android/media/tv/SectionRequest.java
@@ -80,6 +80,11 @@
 
     /**
      * Gets the version number of requested session. If it is null, value will be -1.
+     * <p>The consistency of version numbers between request and response depends on
+     * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value
+     * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be
+     * different from the version of the request. Otherwise, response with a different version from
+     * its request will be considered invalid.
      */
     public int getVersion() {
         return mVersion;
diff --git a/media/java/android/media/tv/SectionResponse.java b/media/java/android/media/tv/SectionResponse.java
index 35836be..f38ea9d 100644
--- a/media/java/android/media/tv/SectionResponse.java
+++ b/media/java/android/media/tv/SectionResponse.java
@@ -74,14 +74,20 @@
     }
 
     /**
-     * Gets the Version number of requested session.
+     * Gets the Version number of requested session. If it is null, value will be -1.
+     * <p>The consistency of version numbers between request and response depends on
+     * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value
+     * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be
+     * different from the version of the request. Otherwise, response with a different version from
+     * its request will be considered invalid.
      */
     public int getVersion() {
         return mVersion;
     }
 
     /**
-     * Gets the raw data of session.
+     * Gets the raw data of session. The sessionData field represents payload data of the session
+     * after session header, which includes version and sessionId.
      */
     @NonNull
     public Bundle getSessionData() {
diff --git a/media/java/android/media/tv/StreamEventResponse.java b/media/java/android/media/tv/StreamEventResponse.java
index f952ce9..28dff37 100644
--- a/media/java/android/media/tv/StreamEventResponse.java
+++ b/media/java/android/media/tv/StreamEventResponse.java
@@ -43,7 +43,7 @@
             };
 
     private final int mEventId;
-    private final long mNpt;
+    private final long mNptMillis;
     private final byte[] mData;
 
     static StreamEventResponse createFromParcelBody(Parcel in) {
@@ -51,17 +51,17 @@
     }
 
     public StreamEventResponse(int requestId, int sequence, @ResponseResult int responseResult,
-            int eventId, long npt, @Nullable byte[] data) {
+            int eventId, long nptMillis, @Nullable byte[] data) {
         super(RESPONSE_TYPE, requestId, sequence, responseResult);
         mEventId = eventId;
-        mNpt = npt;
+        mNptMillis = nptMillis;
         mData = data;
     }
 
     private StreamEventResponse(@NonNull Parcel source) {
         super(RESPONSE_TYPE, source);
         mEventId = source.readInt();
-        mNpt = source.readLong();
+        mNptMillis = source.readLong();
         int dataLength = source.readInt();
         mData = new byte[dataLength];
         source.readByteArray(mData);
@@ -76,9 +76,10 @@
 
     /**
      * Returns the NPT(Normal Play Time) value when the event occurred or will occur.
+     * <p>The time unit of NPT is millisecond.
      */
-    public long getNpt() {
-        return mNpt;
+    public long getNptMillis() {
+        return mNptMillis;
     }
 
     /**
@@ -98,7 +99,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeInt(mEventId);
-        dest.writeLong(mNpt);
+        dest.writeLong(mNptMillis);
         dest.writeInt(mData.length);
         dest.writeByteArray(mData);
     }
diff --git a/media/java/android/media/tv/TableRequest.java b/media/java/android/media/tv/TableRequest.java
index 37df4ea..a1a6b51 100644
--- a/media/java/android/media/tv/TableRequest.java
+++ b/media/java/android/media/tv/TableRequest.java
@@ -91,7 +91,12 @@
     }
 
     /**
-     * Gets the version number of requested table.
+     * Gets the version number of requested table. If it is null, value will be -1.
+     * <p>The consistency of version numbers between request and response depends on
+     * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value
+     * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be
+     * different from the version of the request. Otherwise, response with a different version from
+     * its request will be considered invalid.
      */
     public int getVersion() {
         return mVersion;
diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java
index e9f1136..afc9bee 100644
--- a/media/java/android/media/tv/TableResponse.java
+++ b/media/java/android/media/tv/TableResponse.java
@@ -76,7 +76,12 @@
     }
 
     /**
-     * Gets the Version number of table.
+     * Gets the version number of requested table. If it is null, value will be -1.
+     * <p>The consistency of version numbers between request and response depends on
+     * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value
+     * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be
+     * different from the version of the request. Otherwise, response with a different version from
+     * its request will be considered invalid.
      */
     public int getVersion() {
         return mVersion;
diff --git a/media/java/android/media/tv/TimelineResponse.java b/media/java/android/media/tv/TimelineResponse.java
index fbeb0c4..7de30f5 100644
--- a/media/java/android/media/tv/TimelineResponse.java
+++ b/media/java/android/media/tv/TimelineResponse.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -79,8 +80,8 @@
      * that conveys Time Values on it.
      */
     @Nullable
-    public String getSelector() {
-        return mSelector;
+    public Uri getSelector() {
+        return Uri.parse(mSelector);
     }
 
     /**
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
index aaabe34..8391182 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -23,7 +23,7 @@
 import android.media.tv.interactive.AppLinkInfo;
 import android.media.tv.interactive.ITvInteractiveAppClient;
 import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
-import android.media.tv.interactive.TvInteractiveAppInfo;
+import android.media.tv.interactive.TvInteractiveAppServiceInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.view.Surface;
@@ -33,7 +33,7 @@
  * @hide
  */
 interface ITvInteractiveAppManager {
-    List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId);
+    List<TvInteractiveAppServiceInfo> getTvInteractiveAppServiceList(int userId);
     void prepare(String tiasId, int type, int userId);
     void registerAppLinkInfo(String tiasId, in AppLinkInfo info, int userId);
     void unregisterAppLinkInfo(String tiasId, in AppLinkInfo info, int userId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
index 23be4c6..f410c0f 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
@@ -16,7 +16,7 @@
 
 package android.media.tv.interactive;
 
-import android.media.tv.interactive.TvInteractiveAppInfo;
+import android.media.tv.interactive.TvInteractiveAppServiceInfo;
 
 /**
  * Interface to receive callbacks from ITvInteractiveAppManager regardless of sessions.
@@ -26,6 +26,6 @@
     void onInteractiveAppServiceAdded(in String iAppServiceId);
     void onInteractiveAppServiceRemoved(in String iAppServiceId);
     void onInteractiveAppServiceUpdated(in String iAppServiceId);
-    void onTvInteractiveAppInfoUpdated(in TvInteractiveAppInfo tvIAppInfo);
+    void onTvInteractiveAppServiceInfoUpdated(in TvInteractiveAppServiceInfo tvIAppInfo);
     void onStateChanged(in String iAppServiceId, int type, int state, int err);
 }
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index 9eb4a6c..83ff0f5 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -249,7 +249,7 @@
      *
      * @see #sendAppLinkCommand(String, Bundle)
      * @see #ACTION_APP_LINK_COMMAND
-     * @see android.media.tv.interactive.TvInteractiveAppInfo#getId()
+     * @see android.media.tv.interactive.TvInteractiveAppServiceInfo#getId()
      */
     public static final String INTENT_KEY_INTERACTIVE_APP_SERVICE_ID = "interactive_app_id";
 
@@ -269,7 +269,7 @@
      *
      * @see #sendAppLinkCommand(String, Bundle)
      * @see #ACTION_APP_LINK_COMMAND
-     * @see android.media.tv.interactive.TvInteractiveAppInfo#getSupportedTypes()
+     * @see android.media.tv.interactive.TvInteractiveAppServiceInfo#getSupportedTypes()
      * @see android.media.tv.interactive.TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle)
      */
     public static final String INTENT_KEY_BI_INTERACTIVE_APP_TYPE = "bi_interactive_app_type";
@@ -543,11 +543,11 @@
             }
 
             @Override
-            public void onTvInteractiveAppInfoUpdated(TvInteractiveAppInfo iAppInfo) {
+            public void onTvInteractiveAppServiceInfoUpdated(TvInteractiveAppServiceInfo iAppInfo) {
                 // TODO: add public API updateInteractiveAppInfo()
                 synchronized (mLock) {
                     for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
-                        record.postTvInteractiveAppInfoUpdated(iAppInfo);
+                        record.postTvInteractiveAppServiceInfoUpdated(iAppInfo);
                     }
                 }
             }
@@ -611,16 +611,17 @@
          * This is called when the information about an existing TV Interactive App service has been
          * updated.
          *
-         * <p>Because the system automatically creates a <code>TvInteractiveAppInfo</code> object
-         * for each TV Interactive App service based on the information collected from the
+         * <p>Because the system automatically creates a <code>TvInteractiveAppServiceInfo</code>
+         * object for each TV Interactive App service based on the information collected from the
          * <code>AndroidManifest.xml</code>, this method is only called back when such information
          * has changed dynamically.
          *
-         * @param iAppInfo The <code>TvInteractiveAppInfo</code> object that contains new
+         * @param iAppInfo The <code>TvInteractiveAppServiceInfo</code> object that contains new
          *                 information.
          * @hide
          */
-        public void onTvInteractiveAppInfoUpdated(@NonNull TvInteractiveAppInfo iAppInfo) {
+        public void onTvInteractiveAppServiceInfoUpdated(
+                @NonNull TvInteractiveAppServiceInfo iAppInfo) {
         }
 
         /**
@@ -634,7 +635,7 @@
          */
         public void onTvInteractiveAppServiceStateChanged(
                 @NonNull String iAppServiceId,
-                @TvInteractiveAppInfo.InteractiveAppType int type,
+                @TvInteractiveAppServiceInfo.InteractiveAppType int type,
                 @ServiceState int state,
                 @ErrorCode int err) {
         }
@@ -680,11 +681,12 @@
             });
         }
 
-        public void postTvInteractiveAppInfoUpdated(final TvInteractiveAppInfo iAppInfo) {
+        public void postTvInteractiveAppServiceInfoUpdated(
+                final TvInteractiveAppServiceInfo iAppInfo) {
             mExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onTvInteractiveAppInfoUpdated(iAppInfo);
+                    mCallback.onTvInteractiveAppServiceInfoUpdated(iAppInfo);
                 }
             });
         }
@@ -737,11 +739,11 @@
     /**
      * Returns the complete list of TV Interactive App service on the system.
      *
-     * @return List of {@link TvInteractiveAppInfo} for each TV Interactive App service that
+     * @return List of {@link TvInteractiveAppServiceInfo} for each TV Interactive App service that
      *         describes its meta information.
      */
     @NonNull
-    public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList() {
+    public List<TvInteractiveAppServiceInfo> getTvInteractiveAppServiceList() {
         try {
             return mService.getTvInteractiveAppServiceList(mUserId);
         } catch (RemoteException e) {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index d22fd83..316fbbab 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -242,7 +242,7 @@
     /**
      * Prepares TV Interactive App service for the given type.
      */
-    public abstract void onPrepare(@TvInteractiveAppInfo.InteractiveAppType int type);
+    public abstract void onPrepare(@TvInteractiveAppServiceInfo.InteractiveAppType int type);
 
     /**
      * Called when a request to register an Android application link info record is received.
@@ -277,7 +277,7 @@
     @Nullable
     public abstract Session onCreateSession(
             @NonNull String iAppServiceId,
-            @TvInteractiveAppInfo.InteractiveAppType int type);
+            @TvInteractiveAppServiceInfo.InteractiveAppType int type);
 
     /**
      * Notifies the system when the state of the interactive app RTE has been changed.
@@ -289,7 +289,7 @@
      *              {@link TvInteractiveAppManager#SERVICE_STATE_ERROR}.
      */
     public final void notifyStateChanged(
-            @TvInteractiveAppInfo.InteractiveAppType int type,
+            @TvInteractiveAppServiceInfo.InteractiveAppType int type,
             @TvInteractiveAppManager.ServiceState int state,
             @TvInteractiveAppManager.ErrorCode int error) {
         SomeArgs args = SomeArgs.obtain();
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.aidl
similarity index 94%
rename from media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
rename to media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.aidl
index 5e15016..49600a0 100644
--- a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.aidl
@@ -16,4 +16,4 @@
 
 package android.media.tv.interactive;
 
-parcelable TvInteractiveAppInfo;
\ No newline at end of file
+parcelable TvInteractiveAppServiceInfo;
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.java
similarity index 89%
rename from media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
rename to media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.java
index 6103db0..9c1b242 100644
--- a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.java
@@ -45,9 +45,9 @@
 /**
  * This class is used to specify meta information of a TV interactive app.
  */
-public final class TvInteractiveAppInfo implements Parcelable {
+public final class TvInteractiveAppServiceInfo implements Parcelable {
     private static final boolean DEBUG = false;
-    private static final String TAG = "TvInteractiveAppInfo";
+    private static final String TAG = "TvInteractiveAppServiceInfo";
 
     private static final String XML_START_TAG_NAME = "tv-interactive-app";
 
@@ -71,7 +71,7 @@
     private final String mId;
     private int mTypes;
 
-    public TvInteractiveAppInfo(@NonNull Context context, @NonNull ComponentName component) {
+    public TvInteractiveAppServiceInfo(@NonNull Context context, @NonNull ComponentName component) {
         if (context == null) {
             throw new IllegalArgumentException("context cannot be null.");
         }
@@ -94,28 +94,28 @@
         mId = id;
         mTypes = toTypesFlag(types);
     }
-    private TvInteractiveAppInfo(ResolveInfo service, String id, int types) {
+    private TvInteractiveAppServiceInfo(ResolveInfo service, String id, int types) {
         mService = service;
         mId = id;
         mTypes = types;
     }
 
-    private TvInteractiveAppInfo(@NonNull Parcel in) {
+    private TvInteractiveAppServiceInfo(@NonNull Parcel in) {
         mService = ResolveInfo.CREATOR.createFromParcel(in);
         mId = in.readString();
         mTypes = in.readInt();
     }
 
-    public static final @NonNull Creator<TvInteractiveAppInfo> CREATOR =
-            new Creator<TvInteractiveAppInfo>() {
+    public static final @NonNull Creator<TvInteractiveAppServiceInfo> CREATOR =
+            new Creator<TvInteractiveAppServiceInfo>() {
                 @Override
-                public TvInteractiveAppInfo createFromParcel(Parcel in) {
-                    return new TvInteractiveAppInfo(in);
+                public TvInteractiveAppServiceInfo createFromParcel(Parcel in) {
+                    return new TvInteractiveAppServiceInfo(in);
                 }
 
                 @Override
-                public TvInteractiveAppInfo[] newArray(int size) {
-                    return new TvInteractiveAppInfo[size];
+                public TvInteractiveAppServiceInfo[] newArray(int size) {
+                    return new TvInteractiveAppServiceInfo[size];
                 }
             };
 
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 773e54f..2120511 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -148,12 +148,14 @@
     /**
      * Sets the callback to be invoked when an event is dispatched to this TvInteractiveAppView.
      *
-     * @param callback The callback to receive events. A value of {@code null} removes the existing
-     *                 callback.
+     * @param callback the callback to receive events. MUST NOT be {@code null}.
+     *
+     * @see #clearCallback()
      */
     public void setCallback(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull TvInteractiveAppCallback callback) {
+        com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, callback);
         synchronized (mCallbackLock) {
             mCallbackExecutor = executor;
             mCallback = callback;
@@ -162,6 +164,8 @@
 
     /**
      * Clears the callback.
+     *
+     * @see #setCallback(Executor, TvInteractiveAppCallback)
      */
     public void clearCallback() {
         synchronized (mCallbackLock) {
@@ -389,13 +393,13 @@
      * Prepares the interactive application.
      *
      * @param iAppServiceId the interactive app service ID, which can be found in
-     *                      {@link TvInteractiveAppInfo#getId()}.
+     *                      {@link TvInteractiveAppServiceInfo#getId()}.
      *
      * @see android.media.tv.interactive.TvInteractiveAppManager#getTvInteractiveAppServiceList()
      */
     public void prepareInteractiveApp(
             @NonNull String iAppServiceId,
-            @TvInteractiveAppInfo.InteractiveAppType int type) {
+            @TvInteractiveAppServiceInfo.InteractiveAppType int type) {
         // TODO: document and handle the cases that this method is called multiple times.
         if (DEBUG) {
             Log.d(TAG, "prepareInteractiveApp");
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 55a1998..67fc6c2 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -82,7 +82,7 @@
     <string name="permission_notification">Notifications</string>
 
     <!-- Description of notification permission of COMPUTER profile [CHAR LIMIT=NONE] -->
-    <string name="permission_notification_summary">Can read all notifications, including information like contracts, messages, and photos</string>
+    <string name="permission_notification_summary">Can read all notifications, including information like contacts, messages, and photos</string>
 
     <!-- Storage permission will be granted of COMPUTER profile [CHAR LIMIT=30] -->
     <string name="permission_storage">Photos and media</string>
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
index 2b6570a..74fe4bd 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
@@ -17,6 +17,7 @@
 package android.app.usage;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
@@ -474,10 +475,11 @@
 
     /**
      * Fills the recycled bucket with data of the next bin in the enumeration.
-     * @param bucketOut Bucket to be filled with data.
+     * @param bucketOut Bucket to be filled with data. If null, the method does
+     *                  nothing and returning false.
      * @return true if successfully filled the bucket, false otherwise.
      */
-    public boolean getNextBucket(Bucket bucketOut) {
+    public boolean getNextBucket(@Nullable Bucket bucketOut) {
         if (mSummary != null) {
             return getNextSummaryBucket(bucketOut);
         } else {
@@ -651,7 +653,7 @@
      * @param bucketOut Next item will be set here.
      * @return true if a next item could be set.
      */
-    private boolean getNextSummaryBucket(Bucket bucketOut) {
+    private boolean getNextSummaryBucket(@Nullable Bucket bucketOut) {
         if (bucketOut != null && mEnumerationIndex < mSummary.size()) {
             mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry);
             fillBucketFromSummaryEntry(bucketOut);
@@ -678,7 +680,7 @@
      * @param bucketOut Next item will be set here.
      * @return true if a next item could be set.
      */
-    private boolean getNextHistoryBucket(Bucket bucketOut) {
+    private boolean getNextHistoryBucket(@Nullable Bucket bucketOut) {
         if (bucketOut != null && mHistory != null) {
             if (mEnumerationIndex < mHistory.size()) {
                 mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++,
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index ca080ce..f41475b 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -290,7 +290,7 @@
      *         statistics collection.
      */
     @WorkerThread
-    public Bucket querySummaryForDevice(int networkType, String subscriberId,
+    public Bucket querySummaryForDevice(int networkType, @Nullable String subscriberId,
             long startTime, long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
         try {
@@ -335,8 +335,8 @@
      *         statistics collection.
      */
     @WorkerThread
-    public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime,
-            long endTime) throws SecurityException, RemoteException {
+    public Bucket querySummaryForUser(int networkType, @Nullable String subscriberId,
+            long startTime, long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
         try {
             template = createTemplate(networkType, subscriberId);
@@ -384,7 +384,7 @@
      *         statistics collection.
      */
     @WorkerThread
-    public NetworkStats querySummary(int networkType, String subscriberId, long startTime,
+    public NetworkStats querySummary(int networkType, @Nullable String subscriberId, long startTime,
             long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
         try {
@@ -508,15 +508,17 @@
      *
      * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
      */
+    @NonNull
     @WorkerThread
-    public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
+    public NetworkStats queryDetailsForUid(int networkType, @Nullable String subscriberId,
             long startTime, long endTime, int uid) throws SecurityException {
         return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
             NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
     }
 
     /** @hide */
-    public NetworkStats queryDetailsForUid(NetworkTemplate template,
+    @NonNull
+    public NetworkStats queryDetailsForUid(@NonNull NetworkTemplate template,
             long startTime, long endTime, int uid) throws SecurityException {
         return queryDetailsForUidTagState(template, startTime, endTime, uid,
                 NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
@@ -524,23 +526,59 @@
 
     /**
      * Query network usage statistics details for a given uid and tag.
+     *
+     * This may take a long time, and apps should avoid calling this on their main thread.
+     * Only usable for uids belonging to calling user. Result is not aggregated over time.
+     * This means buckets' start and end timestamps are going to be between 'startTime' and
+     * 'endTime' parameters. The uid is going to be the same as the 'uid' parameter, the tag
+     * the same as the 'tag' parameter, and the state the same as the 'state' parameter.
+     * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+     * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
+     * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
+     * interpolate across partial buckets. Since bucket length is in the order of hours, this
+     * method cannot be used to measure data usage on a fine grained time scale.
      * This may take a long time, and apps should avoid calling this on their main thread.
      *
-     * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
+     * @param networkType As defined in {@link ConnectivityManager}, e.g.
+     *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+     *            etc.
+     * @param subscriberId If applicable, the subscriber id of the network interface.
+     *                     <p>Starting with API level 29, the {@code subscriberId} is guarded by
+     *                     additional restrictions. Calling apps that do not meet the new
+     *                     requirements to access the {@code subscriberId} can provide a {@code
+     *                     null} value when querying for the mobile network type to receive usage
+     *                     for all mobile networks. For additional details see {@link
+     *                     TelephonyManager#getSubscriberId()}.
+     *                     <p>Starting with API level 31, calling apps can provide a
+     *                     {@code subscriberId} with wifi network type to receive usage for
+     *                     wifi networks which is under the given subscription if applicable.
+     *                     Otherwise, pass {@code null} when querying all wifi networks.
+     * @param startTime Start of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @param endTime End of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @param uid UID of app
+     * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data
+     *            across all the tags.
+     * @return Statistics which is described above.
+     * @throws SecurityException if permissions are insufficient to read network statistics.
      */
+    @NonNull
     @WorkerThread
-    public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
+    public NetworkStats queryDetailsForUidTag(int networkType, @Nullable String subscriberId,
             long startTime, long endTime, int uid, int tag) throws SecurityException {
         return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
             tag, NetworkStats.Bucket.STATE_ALL);
     }
 
     /**
-     * Query network usage statistics details for a given uid, tag, and state. Only usable for uids
-     * belonging to calling user. Result is not aggregated over time. This means buckets' start and
-     * end timestamps are going to be between 'startTime' and 'endTime' parameters. The uid is going
-     * to be the same as the 'uid' parameter, the tag the same as the 'tag' parameter, and the state
-     * the same as the 'state' parameter.
+     * Query network usage statistics details for a given uid, tag, and state.
+     *
+     * Only usable for uids belonging to calling user. Result is not aggregated over time.
+     * This means buckets' start and end timestamps are going to be between 'startTime' and
+     * 'endTime' parameters. The uid is going to be the same as the 'uid' parameter, the tag
+     * the same as the 'tag' parameter, and the state the same as the 'state' parameter.
      * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
      * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
      * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
@@ -572,11 +610,12 @@
      *            across all the tags.
      * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
      *            traffic from all states.
-     * @return Statistics object or null if an error happened during statistics collection.
+     * @return Statistics which is described above.
      * @throws SecurityException if permissions are insufficient to read network statistics.
      */
+    @NonNull
     @WorkerThread
-    public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId,
+    public NetworkStats queryDetailsForUidTagState(int networkType, @Nullable String subscriberId,
             long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
         NetworkTemplate template;
         template = createTemplate(networkType, subscriberId);
@@ -669,7 +708,7 @@
      *         statistics collection.
      */
     @WorkerThread
-    public NetworkStats queryDetails(int networkType, String subscriberId, long startTime,
+    public NetworkStats queryDetails(int networkType, @Nullable String subscriberId, long startTime,
             long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
         try {
@@ -698,7 +737,7 @@
      *
      * @hide
      */
-    @SystemApi
+    @SystemApi(client = MODULE_LIBRARIES)
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_STACK})
@@ -724,7 +763,7 @@
      *
      * @hide
      */
-    @SystemApi
+    @SystemApi(client = MODULE_LIBRARIES)
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_STACK})
@@ -785,10 +824,28 @@
     /**
      * Registers to receive notifications about data usage on specified networks.
      *
-     * @see #registerUsageCallback(int, String, long, UsageCallback, Handler)
+     * <p>The callbacks will continue to be called as long as the process is live or
+     * {@link #unregisterUsageCallback} is called.
+     *
+     * @param networkType Type of network to monitor. Either
+    {@link ConnectivityManager#TYPE_MOBILE} or {@link ConnectivityManager#TYPE_WIFI}.
+     * @param subscriberId If applicable, the subscriber id of the network interface.
+     *                     <p>Starting with API level 29, the {@code subscriberId} is guarded by
+     *                     additional restrictions. Calling apps that do not meet the new
+     *                     requirements to access the {@code subscriberId} can provide a {@code
+     *                     null} value when registering for the mobile network type to receive
+     *                     notifications for all mobile networks. For additional details see {@link
+     *                     TelephonyManager#getSubscriberId()}.
+     *                     <p>Starting with API level 31, calling apps can provide a
+     *                     {@code subscriberId} with wifi network type to receive usage for
+     *                     wifi networks which is under the given subscription if applicable.
+     *                     Otherwise, pass {@code null} when querying all wifi networks.
+     * @param thresholdBytes Threshold in bytes to be notified on.
+     * @param callback The {@link UsageCallback} that the system will call when data usage
+     *            has exceeded the specified threshold.
      */
-    public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
-            UsageCallback callback) {
+    public void registerUsageCallback(int networkType, @Nullable String subscriberId,
+            long thresholdBytes, @NonNull UsageCallback callback) {
         registerUsageCallback(networkType, subscriberId, thresholdBytes, callback,
                 null /* handler */);
     }
@@ -818,8 +875,8 @@
      * @param handler to dispatch callback events through, otherwise if {@code null} it uses
      *            the calling thread.
      */
-    public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
-            UsageCallback callback, @Nullable Handler handler) {
+    public void registerUsageCallback(int networkType, @Nullable String subscriberId,
+            long thresholdBytes, @NonNull UsageCallback callback, @Nullable Handler handler) {
         NetworkTemplate template = createTemplate(networkType, subscriberId);
         if (DBG) {
             Log.d(TAG, "registerUsageCallback called with: {"
@@ -839,7 +896,7 @@
      *
      * @param callback The {@link UsageCallback} used when registering.
      */
-    public void unregisterUsageCallback(UsageCallback callback) {
+    public void unregisterUsageCallback(@NonNull UsageCallback callback) {
         if (callback == null || callback.request == null
                 || callback.request.requestId == DataUsageRequest.REQUEST_ID_UNSET) {
             throw new IllegalArgumentException("Invalid UsageCallback");
@@ -880,7 +937,7 @@
         /**
          * Called when data usage has reached the given threshold.
          */
-        public abstract void onThresholdReached(int networkType, String subscriberId);
+        public abstract void onThresholdReached(int networkType, @Nullable String subscriberId);
 
         /**
          * @hide used for internal bookkeeping
@@ -924,7 +981,7 @@
     @RequiresPermission(anyOf = {
             android.Manifest.permission.NETWORK_STATS_PROVIDER,
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
-    @NonNull public void registerNetworkStatsProvider(
+    public void registerNetworkStatsProvider(
             @NonNull String tag,
             @NonNull NetworkStatsProvider provider) {
         try {
@@ -950,7 +1007,7 @@
     @RequiresPermission(anyOf = {
             android.Manifest.permission.NETWORK_STATS_PROVIDER,
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
-    @NonNull public void unregisterNetworkStatsProvider(@NonNull NetworkStatsProvider provider) {
+    public void unregisterNetworkStatsProvider(@NonNull NetworkStatsProvider provider) {
         try {
             provider.getProviderCallbackBinderOrThrow().unregister();
         } catch (RemoteException e) {
@@ -958,7 +1015,7 @@
         }
     }
 
-    private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
+    private static NetworkTemplate createTemplate(int networkType, @Nullable String subscriberId) {
         final NetworkTemplate template;
         switch (networkType) {
             case ConnectivityManager.TYPE_MOBILE:
@@ -1061,9 +1118,9 @@
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_STACK})
-    public void setUidForeground(int uid, boolean uidForeground) {
+    public void noteUidForeground(int uid, boolean uidForeground) {
         try {
-            mService.setUidForeground(uid, uidForeground);
+            mService.noteUidForeground(uid, uidForeground);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
index 43f4c40f..1691942 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
@@ -33,17 +33,20 @@
  */
 @SystemApi
 public final class EthernetNetworkUpdateRequest implements Parcelable {
-    @NonNull
+    @Nullable
     private final IpConfiguration mIpConfig;
     @Nullable
     private final NetworkCapabilities mNetworkCapabilities;
 
     /**
-     * @return the new {@link IpConfiguration}.
+     * Setting the {@link IpConfiguration} is optional in {@link EthernetNetworkUpdateRequest}.
+     * When set to null, the existing IpConfiguration is not updated.
+     *
+     * @return the new {@link IpConfiguration} or null.
      */
-    @NonNull
+    @Nullable
     public IpConfiguration getIpConfiguration() {
-        return new IpConfiguration(mIpConfig);
+        return mIpConfig == null ? null : new IpConfiguration(mIpConfig);
     }
 
     /**
@@ -57,9 +60,8 @@
         return mNetworkCapabilities == null ? null : new NetworkCapabilities(mNetworkCapabilities);
     }
 
-    private EthernetNetworkUpdateRequest(@NonNull final IpConfiguration ipConfig,
+    private EthernetNetworkUpdateRequest(@Nullable final IpConfiguration ipConfig,
             @Nullable final NetworkCapabilities networkCapabilities) {
-        Objects.requireNonNull(ipConfig);
         mIpConfig = ipConfig;
         mNetworkCapabilities = networkCapabilities;
     }
@@ -90,7 +92,8 @@
          */
         public Builder(@NonNull final EthernetNetworkUpdateRequest request) {
             Objects.requireNonNull(request);
-            mBuilderIpConfig = new IpConfiguration(request.mIpConfig);
+            mBuilderIpConfig = null == request.mIpConfig
+                    ? null : new IpConfiguration(request.mIpConfig);
             mBuilderNetworkCapabilities = null == request.mNetworkCapabilities
                     ? null : new NetworkCapabilities(request.mNetworkCapabilities);
         }
@@ -101,8 +104,8 @@
          * @return The builder to facilitate chaining.
          */
         @NonNull
-        public Builder setIpConfiguration(@NonNull final IpConfiguration ipConfig) {
-            mBuilderIpConfig = new IpConfiguration(ipConfig);
+        public Builder setIpConfiguration(@Nullable final IpConfiguration ipConfig) {
+            mBuilderIpConfig = ipConfig == null ? null : new IpConfiguration(ipConfig);
             return this;
         }
 
@@ -119,9 +122,16 @@
 
         /**
          * Build {@link EthernetNetworkUpdateRequest} return the current update request.
+         *
+         * @throws IllegalStateException when both mBuilderNetworkCapabilities and mBuilderIpConfig
+         *                               are null.
          */
         @NonNull
         public EthernetNetworkUpdateRequest build() {
+            if (mBuilderIpConfig == null && mBuilderNetworkCapabilities == null) {
+                throw new IllegalStateException(
+                        "Cannot construct an empty EthernetNetworkUpdateRequest");
+            }
             return new EthernetNetworkUpdateRequest(mBuilderIpConfig, mBuilderNetworkCapabilities);
         }
     }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
index efe626d..c86f7fd 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
@@ -91,7 +91,7 @@
             in INetworkStatsProvider provider);
 
     /** Mark given UID as being in foreground for stats purposes. */
-    void setUidForeground(int uid, boolean uidForeground);
+    void noteUidForeground(int uid, boolean uidForeground);
 
     /** Advise persistence threshold; may be overridden internally. */
     void advisePersistThreshold(long thresholdBytes);
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index 06f2a62..bcfeab9 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
 
 import android.annotation.IntDef;
@@ -124,7 +126,6 @@
     public @Nullable static final String[] INTERFACES_ALL = null;
 
     /** {@link #tag} value for total data across all tags. */
-    // TODO: Rename TAG_NONE to TAG_ALL.
     public static final int TAG_NONE = 0;
 
     /** {@link #metered} value to account for all metered states. */
@@ -390,77 +391,102 @@
 
         /**
          * @return the uid of this entry.
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
         public int getUid() {
             return uid;
         }
 
         /**
          * @return the set state of this entry.
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
         @State public int getSet() {
             return set;
         }
 
         /**
          * @return the tag value of this entry.
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
         public int getTag() {
             return tag;
         }
 
         /**
          * @return the metered state.
+         * @hide
          */
-        @Meteredness public int getMetered() {
+        @Meteredness
+        @SystemApi(client = MODULE_LIBRARIES)
+        public int getMetered() {
             return metered;
         }
 
         /**
          * @return the roaming state.
+         * @hide
          */
-        @Roaming public int getRoaming() {
+        @Roaming
+        @SystemApi(client = MODULE_LIBRARIES)
+        public int getRoaming() {
             return roaming;
         }
 
         /**
          * @return the default network state.
+         * @hide
          */
-        @DefaultNetwork public int getDefaultNetwork() {
+        @DefaultNetwork
+        @SystemApi(client = MODULE_LIBRARIES)
+        public int getDefaultNetwork() {
             return defaultNetwork;
         }
 
         /**
          * @return the number of received bytes.
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
         public long getRxBytes() {
             return rxBytes;
         }
 
         /**
          * @return the number of received packets.
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
         public long getRxPackets() {
             return rxPackets;
         }
 
         /**
          * @return the number of transmitted bytes.
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
         public long getTxBytes() {
             return txBytes;
         }
 
         /**
          * @return the number of transmitted packets.
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
         public long getTxPackets() {
             return txPackets;
         }
 
         /**
          * @return the count of network operations performed for this entry.
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
         public long getOperations() {
             return operations;
         }
@@ -682,7 +708,7 @@
      * The remove() method is not implemented and will throw UnsupportedOperationException.
      * @hide
      */
-    @SystemApi
+    @SystemApi(client = MODULE_LIBRARIES)
     @NonNull public Iterator<Entry> iterator() {
         return new Iterator<Entry>() {
             int mIndex = 0;
diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
index bc836d8..dc4ac55 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
@@ -205,7 +205,7 @@
      *                server context.
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SystemApi(client = MODULE_LIBRARIES)
     @SuppressLint("VisiblySynchronized")
     public static synchronized void init(@NonNull final Context context) {
         if (sStatsService != null) {
@@ -376,7 +376,7 @@
      *
      * @hide
      */
-    @SystemApi
+    @SystemApi(client = MODULE_LIBRARIES)
     public static void setThreadStatsTagDownload() {
         setThreadStatsTag(TAG_SYSTEM_DOWNLOAD);
     }
@@ -468,7 +468,7 @@
      *
      * @see #setThreadStatsTag(int)
      */
-    public static void tagSocket(Socket socket) throws SocketException {
+    public static void tagSocket(@NonNull Socket socket) throws SocketException {
         SocketTagger.get().tag(socket);
     }
 
@@ -483,7 +483,7 @@
      * calling {@code untagSocket()} before sending the socket to another
      * process.
      */
-    public static void untagSocket(Socket socket) throws SocketException {
+    public static void untagSocket(@NonNull Socket socket) throws SocketException {
         SocketTagger.get().untag(socket);
     }
 
@@ -496,14 +496,14 @@
      *
      * @see #setThreadStatsTag(int)
      */
-    public static void tagDatagramSocket(DatagramSocket socket) throws SocketException {
+    public static void tagDatagramSocket(@NonNull DatagramSocket socket) throws SocketException {
         SocketTagger.get().tag(socket);
     }
 
     /**
      * Remove any statistics parameters from the given {@link DatagramSocket}.
      */
-    public static void untagDatagramSocket(DatagramSocket socket) throws SocketException {
+    public static void untagDatagramSocket(@NonNull DatagramSocket socket) throws SocketException {
         SocketTagger.get().untag(socket);
     }
 
@@ -516,7 +516,7 @@
      *
      * @see #setThreadStatsTag(int)
      */
-    public static void tagFileDescriptor(FileDescriptor fd) throws IOException {
+    public static void tagFileDescriptor(@NonNull FileDescriptor fd) throws IOException {
         SocketTagger.get().tag(fd);
     }
 
@@ -524,7 +524,7 @@
      * Remove any statistics parameters from the given {@link FileDescriptor}
      * socket.
      */
-    public static void untagFileDescriptor(FileDescriptor fd) throws IOException {
+    public static void untagFileDescriptor(@NonNull FileDescriptor fd) throws IOException {
         SocketTagger.get().untag(fd);
     }
 
diff --git a/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProviderCallback.aidl b/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
index 7eaa01e..01ff02d 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
@@ -26,6 +26,7 @@
 oneway interface INetworkStatsProviderCallback {
     void notifyStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
     void notifyAlertReached();
-    void notifyWarningOrLimitReached();
+    void notifyWarningReached();
+    void notifyLimitReached();
     void unregister();
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java b/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java
index 23fc069..d37a53d 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java
@@ -152,19 +152,19 @@
         try {
             // Reuse the code path to notify warning reached with limit reached
             // since framework handles them in the same way.
-            getProviderCallbackBinderOrThrow().notifyWarningOrLimitReached();
+            getProviderCallbackBinderOrThrow().notifyWarningReached();
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
     }
 
     /**
-     * Notify system that the quota set by {@link #onSetLimit} or limit set by
+     * Notify system that the limit set by {@link #onSetLimit} or limit set by
      * {@link #onSetWarningAndLimit} has been reached.
      */
     public void notifyLimitReached() {
         try {
-            getProviderCallbackBinderOrThrow().notifyWarningOrLimitReached();
+            getProviderCallbackBinderOrThrow().notifyLimitReached();
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 1d22908..e3794e4 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -1194,7 +1194,7 @@
     }
 
     @VisibleForTesting
-    public void setUidForeground(int uid, boolean uidForeground) {
+    public void noteUidForeground(int uid, boolean uidForeground) {
         PermissionUtils.enforceNetworkStackPermission(mContext);
         synchronized (mStatsLock) {
             final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
@@ -2393,10 +2393,17 @@
         }
 
         @Override
-        public void notifyWarningOrLimitReached() {
-            Log.d(TAG, mTag + ": notifyWarningOrLimitReached");
+        public void notifyWarningReached() {
+            Log.d(TAG, mTag + ": notifyWarningReached");
             BinderUtils.withCleanCallingIdentity(() ->
-                    mNetworkPolicyManager.notifyStatsProviderWarningOrLimitReached());
+                    mNetworkPolicyManager.notifyStatsProviderWarningReached());
+        }
+
+        @Override
+        public void notifyLimitReached() {
+            Log.d(TAG, mTag + ": notifyLimitReached");
+            BinderUtils.withCleanCallingIdentity(() ->
+                    mNetworkPolicyManager.notifyStatsProviderLimitReached());
         }
 
         @Override
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_text_color_primary.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_text_color_primary.xml
new file mode 100644
index 0000000..221d2db
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_text_color_primary.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+        android:alpha="?android:attr/disabledAlpha"
+        android:color="@color/settingslib_text_color_primary_device_default"/>
+    <item android:color="@color/settingslib_text_color_primary_device_default"/>
+</selector>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index 9d39911..cba1a9c 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -17,7 +17,7 @@
 <resources>
     <style name="TextAppearance.PreferenceTitle.SettingsLib"
            parent="@android:style/TextAppearance.Material.Subhead">
-        <item name="android:textColor">@color/settingslib_text_color_primary_device_default</item>
+        <item name="android:textColor">@color/settingslib_text_color_primary</item>
         <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
         <item name="android:textSize">20sp</item>
     </style>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 579e3d7..7439428 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1582,4 +1582,11 @@
     <string name="keyboard_layout_dialog_title">Choose keyboard layout</string>
     <!-- Label of the default keyboard layout.  [CHAR LIMIT=35] -->
     <string name="keyboard_layout_default_label">Default</string>
+
+    <!-- Special access > Title for managing turn screen on settings. [CHAR LIMIT=50] -->
+    <string name="turn_screen_on_title">Turn screen on</string>
+    <!-- Label for a setting which controls whether an app can turn the screen on [CHAR LIMIT=45] -->
+    <string name="allow_turn_screen_on">Allow turning the screen on</string>
+    <!-- Description for a setting which controls whether an app can turn the screen on [CHAR LIMIT=NONE] -->
+    <string name="allow_turn_screen_on_description">Allow an app to turn the screen on. If granted, the app may turn on the screen at any time without your explicit intent.</string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 3d91c5a..2a28891 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -753,7 +753,10 @@
         ParcelUuid[] uuids = mDevice.getUuids();
         if (uuids == null) return false;
 
-        ParcelUuid[] localUuids = mLocalAdapter.getUuids();
+        List<ParcelUuid> uuidsList = mLocalAdapter.getUuidsList();
+        ParcelUuid[] localUuids = new ParcelUuid[uuidsList.size()];
+        uuidsList.toArray(localUuids);
+
         if (localUuids == null) return false;
 
         /*
@@ -1117,7 +1120,8 @@
                 final boolean isOnCall = Utils.isAudioModeOngoingCall(mContext);
                 if ((mIsActiveDeviceHearingAid)
                         || (mIsActiveDeviceHeadset && isOnCall)
-                        || (mIsActiveDeviceA2dp && !isOnCall)) {
+                        || (mIsActiveDeviceA2dp && !isOnCall)
+                        || mIsActiveDeviceLeAudio) {
                     if (isTwsBatteryAvailable(leftBattery, rightBattery) && !shortSummary) {
                         stringRes = R.string.bluetooth_active_battery_level_untethered;
                     } else if (batteryLevelPercentageString != null && !shortSummary) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index e203cba..19df1e9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -246,6 +246,13 @@
         return R.drawable.ic_bt_le_audio;
     }
 
+    public int getAudioLocation(BluetoothDevice device) {
+        if (mService == null || device == null) {
+            return BluetoothLeAudio.AUDIO_LOCATION_INVALID;
+        }
+        return mService.getAudioLocation(device);
+    }
+
     @RequiresApi(Build.VERSION_CODES.S)
     protected void finalize() {
         if (DEBUG) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index e7a6b32..31cc6a4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -126,7 +126,10 @@
     }
 
     public ParcelUuid[] getUuids() {
-        return mAdapter.getUuids();
+        List<ParcelUuid> uuidsList = mAdapter.getUuidsList();
+        ParcelUuid[] uuidsArray = new ParcelUuid[uuidsList.size()];
+        uuidsList.toArray(uuidsArray);
+        return uuidsArray;
     }
 
     public boolean isDiscovering() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
index 0cb2c0b..63a9f0c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
@@ -78,6 +78,11 @@
     static final int REQUEST_CODE_TAKE_PHOTO = 1002;
     static final int REQUEST_CODE_CROP_PHOTO = 1003;
 
+    /**
+     * Delay to allow the photo picker exit animation to complete before the crop activity opens.
+     */
+    private static final long DELAY_BEFORE_CROP_MILLIS = 150;
+
     private static final String IMAGES_DIR = "multi_user";
     private static final String PRE_CROP_PICTURE_FILE_NAME = "PreCropEditUserPhoto.jpg";
     private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
@@ -131,13 +136,15 @@
                 mAvatarUi.returnUriResult(pictureUri);
                 return true;
             case REQUEST_CODE_TAKE_PHOTO:
-            case REQUEST_CODE_CHOOSE_PHOTO:
                 if (mTakePictureUri.equals(pictureUri)) {
                     cropPhoto(pictureUri);
                 } else {
-                    copyAndCropPhoto(pictureUri);
+                    copyAndCropPhoto(pictureUri, false);
                 }
                 return true;
+            case REQUEST_CODE_CHOOSE_PHOTO:
+                copyAndCropPhoto(pictureUri, true);
+                return true;
         }
         return false;
     }
@@ -154,7 +161,7 @@
         mAvatarUi.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO);
     }
 
-    private void copyAndCropPhoto(final Uri pictureUri) {
+    private void copyAndCropPhoto(final Uri pictureUri, boolean delayBeforeCrop) {
         try {
             ThreadUtils.postOnBackgroundThread(() -> {
                 final ContentResolver cr = mContextInjector.getContentResolver();
@@ -165,11 +172,17 @@
                     Log.w(TAG, "Failed to copy photo", e);
                     return;
                 }
-                ThreadUtils.postOnMainThread(() -> {
+                Runnable cropRunnable = () -> {
                     if (!mAvatarUi.isFinishing()) {
                         cropPhoto(mPreCropPictureUri);
                     }
-                });
+                };
+                if (delayBeforeCrop) {
+                    ThreadUtils.postOnMainThreadDelayed(cropRunnable, DELAY_BEFORE_CROP_MILLIS);
+                } else {
+                    ThreadUtils.postOnMainThread(cropRunnable);
+                }
+
             }).get();
         } catch (InterruptedException | ExecutionException e) {
             Log.e(TAG, "Error performing copy-and-crop", e);
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
index 69f1c17..2c1d5da 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
@@ -84,6 +84,13 @@
         getUiThreadHandler().post(runnable);
     }
 
+    /**
+     * Posts the runnable on the main thread with a delay.
+     */
+    public static void postOnMainThreadDelayed(Runnable runnable, long delayMillis) {
+        getUiThreadHandler().postDelayed(runnable, delayMillis);
+    }
+
     private static synchronized ExecutorService getThreadExecutor() {
         if (sThreadExecutor == null) {
             sThreadExecutor = Executors.newFixedThreadPool(
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4b45cc3..e24b2d6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -314,11 +314,6 @@
     <!-- To change system captions state -->
     <uses-permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION" />
 
-    <!-- Compat framework -->
-    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
-    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
-    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
-
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/packages/SystemUI/res/drawable/ic_circular_unchecked.xml b/packages/SystemUI/res/drawable/ic_circular_unchecked.xml
index 9b43cf6..779ab81 100644
--- a/packages/SystemUI/res/drawable/ic_circular_unchecked.xml
+++ b/packages/SystemUI/res/drawable/ic_circular_unchecked.xml
@@ -4,6 +4,6 @@
     android:viewportWidth="24"
     android:viewportHeight="24">
   <path
-      android:fillColor="@color/media_dialog_inactive_item_main_content"
+      android:fillColor="@color/media_dialog_item_main_content"
       android:pathData="M12,22q-2.075,0 -3.9,-0.788 -1.825,-0.787 -3.175,-2.137 -1.35,-1.35 -2.137,-3.175Q2,14.075 2,12t0.788,-3.9q0.787,-1.825 2.137,-3.175 1.35,-1.35 3.175,-2.137Q9.925,2 12,2t3.9,0.788q1.825,0.787 3.175,2.137 1.35,1.35 2.137,3.175Q22,9.925 22,12t-0.788,3.9q-0.787,1.825 -2.137,3.175 -1.35,1.35 -3.175,2.137Q14.075,22 12,22zM12,12zM12,20q3.325,0 5.663,-2.337Q20,15.325 20,12t-2.337,-5.662Q15.325,4 12,4T6.338,6.338Q4,8.675 4,12q0,3.325 2.338,5.663Q8.675,20 12,20z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/media_output_status_check.xml b/packages/SystemUI/res/drawable/media_output_status_check.xml
index 1b750f8..5fbc42b 100644
--- a/packages/SystemUI/res/drawable/media_output_status_check.xml
+++ b/packages/SystemUI/res/drawable/media_output_status_check.xml
@@ -21,6 +21,6 @@
         android:viewportHeight="24"
         android:tint="?attr/colorControlNormal">
     <path
-        android:fillColor="@color/media_dialog_item_status"
+        android:fillColor="@color/media_dialog_item_main_content"
         android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/media_output_status_failed.xml b/packages/SystemUI/res/drawable/media_output_status_failed.xml
index 05c6358..0599e23 100644
--- a/packages/SystemUI/res/drawable/media_output_status_failed.xml
+++ b/packages/SystemUI/res/drawable/media_output_status_failed.xml
@@ -21,6 +21,6 @@
         android:viewportHeight="24"
         android:tint="?attr/colorControlNormal">
     <path
-        android:fillColor="@color/media_dialog_inactive_item_main_content"
+        android:fillColor="@color/media_dialog_item_main_content"
         android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
 </vector>
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index eeb37c7..20747fa 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -83,7 +83,7 @@
                 android:ellipsize="end"
                 android:maxLines="1"
                 android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-                android:textColor="@color/media_dialog_inactive_item_main_content"
+                android:textColor="@color/media_dialog_item_main_content"
                 android:textSize="16sp"/>
             <TextView
                 android:id="@+id/subtitle"
@@ -91,7 +91,7 @@
                 android:layout_height="wrap_content"
                 android:ellipsize="end"
                 android:maxLines="1"
-                android:textColor="@color/media_dialog_inactive_item_main_content"
+                android:textColor="@color/media_dialog_item_main_content"
                 android:textSize="14sp"
                 android:fontFamily="@*android:string/config_bodyFontFamily"
                 android:visibility="gone"/>
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
index ad2bc79..5aa6080 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -37,7 +37,5 @@
             android:layout_gravity="center"
             android:minWidth="@dimen/ongoing_appops_chip_min_width"
             android:maxWidth="@dimen/ongoing_appops_chip_max_width"
-            android:clipChildren="false"
-            android:clipToPadding="false"
             />
 </com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 4b96d5d..3a638b1 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -67,10 +67,12 @@
 
     <!-- media output dialog-->
     <color name="media_dialog_background">@color/material_dynamic_neutral10</color>
-    <color name="media_dialog_active_item_main_content">@color/material_dynamic_neutral10</color>
-    <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_neutral10</color>
-    <color name="media_dialog_item_status">@color/material_dynamic_neutral10</color>
-    <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
+    <color name="media_dialog_item_main_content">@color/material_dynamic_primary90</color>
+    <color name="media_dialog_item_background">@color/material_dynamic_neutral_variant20</color>
+    <color name="media_dialog_connected_item_background">@color/material_dynamic_secondary20</color>
+    <color name="media_dialog_seekbar_progress">@color/material_dynamic_secondary40</color>
+    <color name="media_dialog_button_background">@color/material_dynamic_primary70</color>
+    <color name="media_dialog_solid_button_text">@color/material_dynamic_secondary20</color>
 
     <!-- Biometric dialog colors -->
     <color name="biometric_dialog_gray">#ffcccccc</color>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index c56ba7b..740697b 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -37,6 +37,7 @@
     <dimen name="controls_task_view_right_margin">8dp</dimen>
 
     <dimen name="split_shade_header_height">42dp</dimen>
+    <dimen name="status_bar_header_height_keyguard">42dp</dimen>
 
     <!-- Distance that the full shade transition takes in order to complete by tapping on a button
          like "expand". -->
@@ -53,6 +54,14 @@
          the shade (in alpha) -->
     <dimen name="lockscreen_shade_scrim_transition_distance">80dp</dimen>
 
+    <!-- The notifications scrim transition should start when the other scrims' transition is at
+         95%. -->
+    <dimen name="lockscreen_shade_notifications_scrim_transition_delay">76dp</dimen>
+
+    <!-- The notifications scrim transition duration is 66.6% of the duration of the other scrims'
+         transition. -->
+    <dimen name="lockscreen_shade_notifications_scrim_transition_distance">53.28dp</dimen>
+
     <!-- Distance that the full shade transition takes in order for the keyguard content on
          NotificationPanelViewController to fully fade (e.g. Clock & Smartspace) -->
     <dimen name="lockscreen_shade_npvc_keyguard_content_alpha_transition_distance">80dp</dimen>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index f267088..bdd7049 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -24,6 +24,7 @@
     <dimen name="keyguard_split_shade_top_margin">72dp</dimen>
 
     <dimen name="split_shade_header_height">56dp</dimen>
+    <dimen name="status_bar_header_height_keyguard">56dp</dimen>
 
     <dimen name="qs_media_session_height_expanded">184dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 1edaaad..49fc848 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -176,10 +176,12 @@
 
     <!-- media output dialog-->
     <color name="media_dialog_background" android:lstar="98">@color/material_dynamic_neutral90</color>
-    <color name="media_dialog_active_item_main_content">@color/material_dynamic_primary10</color>
-    <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_primary40</color>
-    <color name="media_dialog_item_status">@color/material_dynamic_primary10</color>
+    <color name="media_dialog_item_main_content">@color/material_dynamic_primary20</color>
     <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
+    <color name="media_dialog_connected_item_background">@color/material_dynamic_primary90</color>
+    <color name="media_dialog_seekbar_progress">@color/material_dynamic_secondary40</color>
+    <color name="media_dialog_button_background">@color/material_dynamic_primary40</color>
+    <color name="media_dialog_solid_button_text">@color/material_dynamic_neutral95</color>
 
     <!-- controls -->
     <color name="control_primary_text">#E6FFFFFF</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ffae601..6871310 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -941,6 +941,9 @@
     <!--  Three privacy items. This value must not be exceeded  -->
     <dimen name="ongoing_appops_chip_max_width">76dp</dimen>
     <dimen name="ongoing_appops_dot_diameter">6dp</dimen>
+    <dimen name="ongoing_appops_chip_min_animation_width">10dp</dimen>
+    <dimen name="ongoing_appops_chip_animation_in_status_bar_translation_x">15dp</dimen>
+    <dimen name="ongoing_appops_chip_animation_out_status_bar_translation_x">7dp</dimen>
     <!--  Total minimum padding to enforce to ensure that the dot can always show  -->
     <dimen name="ongoing_appops_dot_min_padding">20dp</dimen>
 
@@ -1138,6 +1141,12 @@
          the shade (in alpha) -->
     <dimen name="lockscreen_shade_scrim_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
 
+    <!-- Distance that it takes in order for the notifications scrim fade in to start. -->
+    <dimen name="lockscreen_shade_notifications_scrim_transition_delay">0dp</dimen>
+
+    <!-- Distance that it takes for the notifications scrim to fully fade if after it started. -->
+    <dimen name="lockscreen_shade_notifications_scrim_transition_distance">@dimen/lockscreen_shade_scrim_transition_distance</dimen>
+
     <!-- Distance that the full shade transition takes in order for the keyguard content on
          NotificationPanelViewController to fully fade (e.g. Clock & Smartspace) -->
     <dimen name="lockscreen_shade_npvc_keyguard_content_alpha_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3ae21e0..f5c1382 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -486,7 +486,7 @@
 
     <style name="MediaOutputItemInactiveTitle">
         <item name="android:textSize">16sp</item>
-        <item name="android:textColor">@color/media_dialog_inactive_item_main_content</item>
+        <item name="android:textColor">@color/media_dialog_item_main_content</item>
     </style>
 
     <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 7e1a026..807ff21 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -254,8 +254,8 @@
 
         mMirrorViewGeometryVsyncCallback =
                 l -> {
-                    if (isWindowVisible() && mMirrorSurface != null) {
-                        calculateSourceBounds(mMagnificationFrame, mScale);
+                    if (isWindowVisible() && mMirrorSurface != null && calculateSourceBounds(
+                            mMagnificationFrame, mScale)) {
                         // The final destination for the magnification surface should be at 0,0
                         // since the ViewRootImpl's position will change
                         mTmpRect.set(0, 0, mMagnificationFrame.width(),
@@ -350,6 +350,7 @@
             mMirrorWindowControl.destroyControl();
         }
         mMirrorViewBounds.setEmpty();
+        mSourceBounds.setEmpty();
         updateSystemUIStateIfNeeded();
         mContext.unregisterComponentCallbacks(this);
     }
@@ -728,8 +729,12 @@
     /**
      * Calculates the desired source bounds. This will be the area under from the center of  the
      * displayFrame, factoring in scale.
+     *
+     * @return {@code true} if the source bounds is changed.
      */
-    private void calculateSourceBounds(Rect displayFrame, float scale) {
+    private boolean calculateSourceBounds(Rect displayFrame, float scale) {
+        final Rect oldSourceBounds = mTmpRect;
+        oldSourceBounds.set(mSourceBounds);
         int halfWidth = displayFrame.width() / 2;
         int halfHeight = displayFrame.height() / 2;
         int left = displayFrame.left + (halfWidth - (int) (halfWidth / scale));
@@ -757,6 +762,7 @@
             mSourceBounds.offsetTo(mSourceBounds.left,
                     mWindowBounds.height() - mSourceBounds.height());
         }
+        return !mSourceBounds.equals(oldSourceBounds);
     }
 
     private void calculateMagnificationFrameBoundary() {
@@ -1079,7 +1085,7 @@
         pw.println("      mMagnificationFrame:"
                 + (isWindowVisible() ? mMagnificationFrame : "empty"));
         pw.println("      mSourceBounds:"
-                 + (isWindowVisible() ? mSourceBounds : "empty"));
+                 + (mSourceBounds.isEmpty() ? "empty" : mSourceBounds));
         pw.println("      mSystemGestureTop:" + mSystemGestureTop);
         pw.println("      mMagnificationFrameOffsetX:" + mMagnificationFrameOffsetX);
         pw.println("      mMagnificationFrameOffsetY:" + mMagnificationFrameOffsetY);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 64c2d2e..c100a07 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -726,8 +726,8 @@
 
         boolean isCameraPrivacyEnabled = false;
         if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE
-                && mSensorPrivacyManager.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA,
-                mCurrentDialogArgs.argi1 /* userId */)) {
+                && mSensorPrivacyManager.isSensorPrivacyEnabled(
+                SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, SensorPrivacyManager.Sensors.CAMERA)) {
             isCameraPrivacyEnabled = true;
         }
         // TODO(b/141025588): Create separate methods for handling hard and soft errors.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index a27b9cd..b811c51 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -116,7 +116,7 @@
     }
 
     fun showRipple(biometricSourceType: BiometricSourceType?) {
-        if (!keyguardUpdateMonitor.isKeyguardVisible ||
+        if (!(keyguardUpdateMonitor.isKeyguardVisible || keyguardUpdateMonitor.isDreaming) ||
             keyguardUpdateMonitor.userNeedsStrongAuth()) {
             return
         }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 61cfe92..2db3de1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -145,7 +145,7 @@
     /***************************************/
     // 900 - media
     public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
-    public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
+    public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true);
     public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, true);
     public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
     public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 736e2e0..acad30b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -20,6 +20,7 @@
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_UNLOCK_ANIMATION;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
@@ -123,11 +124,11 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -1557,6 +1558,7 @@
     public void setOccluded(boolean isOccluded, boolean animate) {
         Trace.beginSection("KeyguardViewMediator#setOccluded");
         if (DEBUG) Log.d(TAG, "setOccluded " + isOccluded);
+        mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_TRANSITION_FROM_AOD);
         mHandler.removeMessages(SET_OCCLUDED);
         Message msg = mHandler.obtainMessage(SET_OCCLUDED, isOccluded ? 1 : 0, animate ? 1 : 0);
         mHandler.sendMessage(msg);
@@ -2805,6 +2807,7 @@
             RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
             RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
         Trace.beginSection("KeyguardViewMediator#startKeyguardExitAnimation");
+        mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_TRANSITION_FROM_AOD);
         Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
                 new StartKeyguardExitAnimParams(transit, startTime, fadeoutDuration, apps,
                         wallpapers, nonApps, finishedCallback));
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index ffdd537..510d15b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -137,6 +137,7 @@
     private MediaCarouselController mMediaCarouselController;
     private final MediaOutputDialogFactory mMediaOutputDialogFactory;
     private final FalsingManager mFalsingManager;
+    private final MediaFlags mMediaFlags;
 
     // Used for swipe-to-dismiss logging.
     protected boolean mIsImpressed = false;
@@ -155,7 +156,7 @@
             Lazy<MediaDataManager> lazyMediaDataManager,
             MediaOutputDialogFactory mediaOutputDialogFactory,
             MediaCarouselController mediaCarouselController,
-            FalsingManager falsingManager, SystemClock systemClock) {
+            FalsingManager falsingManager, MediaFlags mediaFlags, SystemClock systemClock) {
         mContext = context;
         mBackgroundExecutor = backgroundExecutor;
         mActivityStarter = activityStarter;
@@ -166,6 +167,7 @@
         mMediaOutputDialogFactory = mediaOutputDialogFactory;
         mMediaCarouselController = mediaCarouselController;
         mFalsingManager = falsingManager;
+        mMediaFlags = mediaFlags;
         mSystemClock = systemClock;
         loadDimens();
 
@@ -504,8 +506,9 @@
         List<MediaAction> actionIcons = data.getActions();
         List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact();
 
-        // If we got session actions, use those instead
-        if (data.getSemanticActions() != null) {
+        // If the session actions flag is enabled, but we're still using the regular layout, use
+        // the session actions anyways
+        if (mMediaFlags.areMediaSessionActionsEnabled() && data.getSemanticActions() != null) {
             MediaButton semanticActions = data.getSemanticActions();
 
             actionIcons = new ArrayList<MediaAction>();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index ea92abd..9e14fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -610,8 +610,7 @@
         var actionIcons: List<MediaAction> = emptyList()
         var actionsToShowCollapsed: List<Int> = emptyList()
         var semanticActions: MediaButton? = null
-        if (mediaFlags.areMediaSessionActionsEnabled(sbn.packageName, sbn.user.identifier) &&
-                mediaController.playbackState != null) {
+        if (mediaFlags.areMediaSessionActionsEnabled() && mediaController.playbackState != null) {
             semanticActions = createActionsFromState(sbn.packageName, mediaController)
         } else {
             val actions = createActionsFromNotification(sbn)
@@ -727,7 +726,7 @@
                 }
             }
 
-            // Finally, assign the remaining button slots: play/pause A B C D
+            // Finally, assign the remaining button slots: C A play/pause B D
             // A = previous, else custom action (if not reserved)
             // B = next, else custom action (if not reserved)
             // C and D are always custom actions
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
index f5200f9..dd35a9a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.media
 
-import android.app.StatusBarManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
@@ -27,17 +26,16 @@
     /**
      * Check whether media control actions should be based on PlaybackState instead of notification
      */
-    fun areMediaSessionActionsEnabled(packageName: String, userId: Int): Boolean {
-        val enabled = StatusBarManager.useMediaSessionActionsForApp(packageName, userId)
-        // Allow global override with flag
-        return enabled || featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS)
+    fun areMediaSessionActionsEnabled(): Boolean {
+        return featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS)
     }
 
     /**
      * Check whether media controls should use the new session-based layout
      */
     fun useMediaSessionLayout(): Boolean {
-        return featureFlags.isEnabled(Flags.MEDIA_SESSION_LAYOUT)
+        return featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS) &&
+            featureFlags.isEnabled(Flags.MEDIA_SESSION_LAYOUT)
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 7a4dee2..d472aee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -812,7 +812,7 @@
     @TransformationType
     fun calculateTransformationType(): Int {
         if (isTransitioningToFullShade) {
-            if (inSplitShade) {
+            if (inSplitShade && areGuidedTransitionHostsVisible()) {
                 return TRANSFORMATION_TYPE_TRANSITION
             }
             return TRANSFORMATION_TYPE_FADE
@@ -829,6 +829,11 @@
         return TRANSFORMATION_TYPE_TRANSITION
     }
 
+    private fun areGuidedTransitionHostsVisible(): Boolean {
+        return getHost(previousLocation)?.visible == true &&
+                getHost(desiredLocation)?.visible == true
+    }
+
     /**
      * @return the current transformation progress if we're in a guided transformation and -1
      * otherwise
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 0b23ad5..a646482 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -119,7 +119,9 @@
             mCheckBox.setVisibility(View.GONE);
             mStatusIcon.setVisibility(View.GONE);
             mContainerLayout.setOnClickListener(null);
-            mTitleText.setTextColor(mController.getColorInactiveItem());
+            mTitleText.setTextColor(mController.getColorItemContent());
+            mSubTitleText.setTextColor(mController.getColorItemContent());
+            mTwoLineTitleText.setTextColor(mController.getColorItemContent());
             mSeekBar.getProgressDrawable().setColorFilter(
                     new PorterDuffColorFilter(mController.getColorSeekbarProgress(),
                             PorterDuff.Mode.SRC_IN));
@@ -140,7 +142,7 @@
                         && !mController.hasAdjustVolumeUserRestriction()) {
                     mProgressBar.getIndeterminateDrawable().setColorFilter(
                             new PorterDuffColorFilter(
-                                    mController.getColorInactiveItem(),
+                                    mController.getColorItemContent(),
                                     PorterDuff.Mode.SRC_IN));
                     setSingleLineLayout(getItemTitle(device), true /* bFocused */,
                             false /* showSeekBar*/,
@@ -155,7 +157,7 @@
                     mTitleIcon.setAlpha(DEVICE_CONNECTED_ALPHA);
                     mStatusIcon.setImageDrawable(
                             mContext.getDrawable(R.drawable.media_output_status_failed));
-                    mStatusIcon.setColorFilter(mController.getColorInactiveItem());
+                    mStatusIcon.setColorFilter(mController.getColorItemContent());
                     setTwoLineLayout(device, false /* bFocused */,
                             false /* showSeekBar */, false /* showProgressBar */,
                             true /* showSubtitle */, true /* showStatus */);
@@ -163,7 +165,7 @@
                     mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
                 } else if (mController.getSelectedMediaDevice().size() > 1
                         && isDeviceIncluded(mController.getSelectedMediaDevice(), device)) {
-                    mTitleText.setTextColor(mController.getColorActiveItem());
+                    mTitleText.setTextColor(mController.getColorItemContent());
                     setSingleLineLayout(getItemTitle(device), true /* bFocused */,
                             true /* showSeekBar */,
                             false /* showProgressBar */, false /* showStatus */);
@@ -173,13 +175,13 @@
                     mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
                         onCheckBoxClicked(false, device);
                     });
-                    setCheckBoxColor(mCheckBox, mController.getColorActiveItem());
+                    setCheckBoxColor(mCheckBox, mController.getColorItemContent());
                     initSeekbar(device);
                 } else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
                     mStatusIcon.setImageDrawable(
                             mContext.getDrawable(R.drawable.media_output_status_check));
-                    mStatusIcon.setColorFilter(mController.getColorActiveItem());
-                    mTitleText.setTextColor(mController.getColorActiveItem());
+                    mStatusIcon.setColorFilter(mController.getColorItemContent());
+                    mTitleText.setTextColor(mController.getColorItemContent());
                     setSingleLineLayout(getItemTitle(device), true /* bFocused */,
                             true /* showSeekBar */,
                             false /* showProgressBar */, true /* showStatus */);
@@ -192,7 +194,7 @@
                     mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
                         onCheckBoxClicked(true, device);
                     });
-                    setCheckBoxColor(mCheckBox, mController.getColorInactiveItem());
+                    setCheckBoxColor(mCheckBox, mController.getColorItemContent());
                     setSingleLineLayout(getItemTitle(device), false /* bFocused */,
                             false /* showSeekBar */,
                             false /* showProgressBar */, false /* showStatus */);
@@ -214,7 +216,7 @@
         @Override
         void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
             if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
-                mTitleText.setTextColor(mController.getColorInactiveItem());
+                mTitleText.setTextColor(mController.getColorItemContent());
                 mCheckBox.setVisibility(View.GONE);
                 setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
                         false /* bFocused */);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 62d5c8e..df0c14b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -170,14 +170,16 @@
         void setSingleLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar,
                 boolean showProgressBar, boolean showStatus) {
             mTwoLineLayout.setVisibility(View.GONE);
+            boolean isActive = showSeekBar || showProgressBar;
             final Drawable backgroundDrawable =
-                    showSeekBar || showProgressBar
+                    isActive
                             ? mContext.getDrawable(R.drawable.media_output_item_background_active)
                                     .mutate() : mContext.getDrawable(
                             R.drawable.media_output_item_background)
                             .mutate();
             backgroundDrawable.setColorFilter(new PorterDuffColorFilter(
-                    mController.getColorItemBackground(),
+                    isActive ? mController.getColorConnectedItemBackground()
+                            : mController.getColorItemBackground(),
                     PorterDuff.Mode.SRC_IN));
             mItemLayout.setBackground(backgroundDrawable);
             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
@@ -366,7 +368,7 @@
                     .mutate();
             drawable.setColorFilter(
                     new PorterDuffColorFilter(Utils.getColorStateListDefaultColor(mContext,
-                            R.color.media_dialog_active_item_main_content),
+                            R.color.media_dialog_item_main_content),
                             PorterDuff.Mode.SRC_IN));
             return drawable;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index a8141c0..dcb1c7c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -216,6 +216,7 @@
                         PorterDuff.Mode.SRC_IN);
                 mDoneButton.getBackground().setColorFilter(buttonColorFilter);
                 mStopButton.getBackground().setColorFilter(buttonColorFilter);
+                mDoneButton.setTextColor(mAdapter.getController().getColorPositiveButtonText());
             }
             mHeaderIcon.setVisibility(View.VISIBLE);
             mHeaderIcon.setImageIcon(icon);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 0b6c68d..ea7f7f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -110,11 +110,12 @@
 
     private MediaOutputMetricLogger mMetricLogger;
 
-    private int mColorActiveItem;
-    private int mColorInactiveItem;
+    private int mColorItemContent;
     private int mColorSeekbarProgress;
     private int mColorButtonBackground;
     private int mColorItemBackground;
+    private int mColorConnectedItemBackground;
+    private int mColorPositiveButtonText;
 
     @Inject
     public MediaOutputController(@NonNull Context context, String packageName,
@@ -133,16 +134,18 @@
         mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
         mDialogLaunchAnimator = dialogLaunchAnimator;
         mNearbyMediaDevicesManager = nearbyMediaDevicesManagerOptional.orElse(null);
-        mColorActiveItem = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_active_item_main_content);
-        mColorInactiveItem = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_inactive_item_main_content);
+        mColorItemContent = Utils.getColorStateListDefaultColor(mContext,
+                R.color.media_dialog_item_main_content);
         mColorSeekbarProgress = Utils.getColorStateListDefaultColor(mContext,
-                android.R.color.system_accent1_200);
+                R.color.media_dialog_seekbar_progress);
         mColorButtonBackground = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_item_background);
+                R.color.media_dialog_button_background);
         mColorItemBackground = Utils.getColorStateListDefaultColor(mContext,
-                android.R.color.system_accent2_50);
+                R.color.media_dialog_item_background);
+        mColorConnectedItemBackground = Utils.getColorStateListDefaultColor(mContext,
+                R.color.media_dialog_connected_item_background);
+        mColorPositiveButtonText = Utils.getColorStateListDefaultColor(mContext,
+                R.color.media_dialog_solid_button_text);
     }
 
     void start(@NonNull Callback cb) {
@@ -322,8 +325,7 @@
     }
 
     void setColorFilter(Drawable drawable, boolean isActive) {
-        drawable.setColorFilter(new PorterDuffColorFilter(isActive
-                ? mColorActiveItem : mColorInactiveItem,
+        drawable.setColorFilter(new PorterDuffColorFilter(mColorItemContent,
                 PorterDuff.Mode.SRC_IN));
     }
 
@@ -358,26 +360,32 @@
         ColorScheme mCurrentColorScheme = new ColorScheme(wallpaperColors,
                 isDarkTheme);
         if (isDarkTheme) {
-            mColorActiveItem = mCurrentColorScheme.getNeutral1().get(10);
-            mColorInactiveItem = mCurrentColorScheme.getNeutral1().get(10);
-            mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(2);
-            mColorButtonBackground = mCurrentColorScheme.getAccent1().get(2);
-            mColorItemBackground = mCurrentColorScheme.getAccent2().get(0);
+            mColorItemContent = mCurrentColorScheme.getAccent1().get(2); // A1-100
+            mColorSeekbarProgress = mCurrentColorScheme.getAccent2().get(7); // A2-600
+            mColorButtonBackground = mCurrentColorScheme.getAccent1().get(4); // A1-300
+            mColorItemBackground = mCurrentColorScheme.getNeutral2().get(9); // N2-800
+            mColorConnectedItemBackground = mCurrentColorScheme.getAccent2().get(9); // A2-800
+            mColorPositiveButtonText = mCurrentColorScheme.getAccent2().get(9); // A2-800
         } else {
-            mColorActiveItem = mCurrentColorScheme.getNeutral1().get(10);
-            mColorInactiveItem = mCurrentColorScheme.getAccent1().get(7);
-            mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(3);
-            mColorButtonBackground = mCurrentColorScheme.getAccent1().get(3);
-            mColorItemBackground = mCurrentColorScheme.getAccent2().get(0);
+            mColorItemContent = mCurrentColorScheme.getAccent1().get(9); // A1-800
+            mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(4); // A1-300
+            mColorButtonBackground = mCurrentColorScheme.getAccent1().get(7); // A1-600
+            mColorItemBackground = mCurrentColorScheme.getAccent2().get(1); // A2-50
+            mColorConnectedItemBackground = mCurrentColorScheme.getAccent1().get(2); // A1-100
+            mColorPositiveButtonText = mCurrentColorScheme.getNeutral1().get(1); // N1-50
         }
     }
 
-    public int getColorActiveItem() {
-        return mColorActiveItem;
+    public int getColorConnectedItemBackground() {
+        return mColorConnectedItemBackground;
     }
 
-    public int getColorInactiveItem() {
-        return mColorInactiveItem;
+    public int getColorPositiveButtonText() {
+        return mColorPositiveButtonText;
+    }
+
+    public int getColorItemContent() {
+        return mColorItemContent;
     }
 
     public int getColorSeekbarProgress() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 71cacac..3d5b3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -32,6 +32,7 @@
 import android.view.ViewGroup
 import android.view.WindowManager
 import com.android.internal.widget.CachingIconView
+import com.android.settingslib.Utils
 import com.android.systemui.R
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.statusbar.gesture.TapGestureDetector
@@ -150,40 +151,36 @@
         appNameOverride: CharSequence? = null,
     ) {
         val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon)
-        appIconView.contentDescription = appNameOverride ?: getAppName(appPackageName)
-
-        val appIcon = appIconDrawableOverride ?: getAppIcon(appPackageName)
-        val visibility = if (appIcon != null) {
-            View.VISIBLE
-        } else {
-            View.GONE
-        }
-        appIconView.setImageDrawable(appIcon)
-        appIconView.visibility = visibility
+        val appInfo = getAppInfo(appPackageName)
+        appIconView.contentDescription = appNameOverride ?: appInfo.appName
+        appIconView.setImageDrawable(appIconDrawableOverride ?: appInfo.appIcon)
     }
 
-    /** Returns the icon of the app playing the media or null if we can't find it. */
-    private fun getAppIcon(appPackageName: String?): Drawable? {
-        appPackageName ?: return null
-        return try {
-            context.packageManager.getApplicationIcon(appPackageName)
-        } catch (e: PackageManager.NameNotFoundException) {
-            Log.w(TAG, "Cannot find icon for package $appPackageName", e)
-            null
+    /**
+     * Returns the app name and icon of the app playing media, or a default name and icon if we
+     * can't find the app name/icon.
+     */
+    private fun getAppInfo(appPackageName: String?): AppInfo {
+        if (appPackageName != null) {
+            try {
+                return AppInfo(
+                    appName = context.packageManager.getApplicationInfo(
+                        appPackageName, PackageManager.ApplicationInfoFlags.of(0)
+                    ).loadLabel(context.packageManager).toString(),
+                    appIcon = context.packageManager.getApplicationIcon(appPackageName)
+                )
+            } catch (e: PackageManager.NameNotFoundException) {
+                Log.w(TAG, "Cannot find package $appPackageName", e)
+            }
         }
-    }
-
-    /** Returns the name of the app playing the media or null if we can't find it. */
-    private fun getAppName(appPackageName: String?): String? {
-        appPackageName ?: return null
-        return try {
-            context.packageManager.getApplicationInfo(
-                    appPackageName, PackageManager.ApplicationInfoFlags.of(0)
-            ).loadLabel(context.packageManager).toString()
-        } catch (e: PackageManager.NameNotFoundException) {
-            Log.w(TAG, "Cannot find name for package $appPackageName", e)
-            null
-        }
+        return AppInfo(
+            appName = context.getString(R.string.media_output_dialog_unknown_launch_app_name),
+            appIcon = context.resources.getDrawable(R.drawable.ic_cast).apply {
+                this.setTint(
+                    Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
+                )
+            }
+        )
     }
 
     private fun onScreenTapped(e: MotionEvent) {
@@ -205,3 +202,8 @@
     const val REASON_TIMEOUT = "TIMEOUT"
     const val REASON_SCREEN_TAP = "SCREEN_TAP"
 }
+
+private data class AppInfo(
+    val appName: String,
+    val appIcon: Drawable
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index ab4d0dd..de3e89d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -116,6 +116,16 @@
     private var scrimTransitionDistance = 0
 
     /**
+     * Distance that it takes in order for the notifications scrim fade in to start.
+     */
+    private var notificationsScrimTransitionDelay = 0
+
+    /**
+     * Distance that it takes for the notifications scrim to fully fade if after it started.
+     */
+    private var notificationsScrimTransitionDistance = 0
+
+    /**
      * Distance that the full shade transition takes in order for the notification shelf to fully
      * expand.
      */
@@ -225,6 +235,10 @@
             R.dimen.lockscreen_shade_transition_by_tap_distance)
         scrimTransitionDistance = context.resources.getDimensionPixelSize(
                 R.dimen.lockscreen_shade_scrim_transition_distance)
+        notificationsScrimTransitionDelay = context.resources.getDimensionPixelSize(
+                R.dimen.lockscreen_shade_notifications_scrim_transition_delay)
+        notificationsScrimTransitionDistance = context.resources.getDimensionPixelSize(
+                R.dimen.lockscreen_shade_notifications_scrim_transition_distance)
         notificationShelfTransitionDistance = context.resources.getDimensionPixelSize(
                 R.dimen.lockscreen_shade_notif_shelf_transition_distance)
         qsTransitionDistance = context.resources.getDimensionPixelSize(
@@ -405,6 +419,7 @@
                             false /* animate */, 0 /* delay */)
 
                     mediaHierarchyManager.setTransitionToFullShadeAmount(field)
+                    transitionToShadeAmountScrim(field)
                     transitionToShadeAmountCommon(field)
                     transitionToShadeAmountKeyguard(field)
                 }
@@ -417,10 +432,15 @@
     var qSDragProgress = 0f
         private set
 
-    private fun transitionToShadeAmountCommon(dragDownAmount: Float) {
+    private fun transitionToShadeAmountScrim(dragDownAmount: Float) {
         val scrimProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
-        scrimController.setTransitionToFullShadeProgress(scrimProgress)
+        val notificationsScrimDragAmount = dragDownAmount - notificationsScrimTransitionDelay
+        val notificationsScrimProgress = MathUtils.saturate(
+                notificationsScrimDragAmount / notificationsScrimTransitionDistance)
+        scrimController.setTransitionToFullShadeProgress(scrimProgress, notificationsScrimProgress)
+    }
 
+    private fun transitionToShadeAmountCommon(dragDownAmount: Float) {
         if (depthControllerTransitionDistance > 0) {
             val depthProgress =
                 MathUtils.saturate(dragDownAmount / depthControllerTransitionDistance)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 3dd717d..eaa66bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -226,10 +226,7 @@
                 ? MathUtils.lerp(shortestWidth, getWidth(), fractionToShade)
                 : getWidth();
         ActivatableNotificationView anv = (ActivatableNotificationView) this;
-        NotificationBackgroundView bg = anv.getBackgroundNormal();
-        if (bg != null) {
-            anv.getBackgroundNormal().setActualWidth((int) actualWidth);
-        }
+        anv.setBackgroundWidth((int) actualWidth);
         if (mShelfIcons != null) {
             mShelfIcons.setActualLayoutWidth((int) actualWidth);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 465ab93..3013ad0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -188,7 +188,7 @@
 
         setContentDescription(state.contentDescription);
         int newVisibility = state.visible && !mForceHidden ? View.VISIBLE : View.GONE;
-        if (newVisibility != mMobileGroup.getVisibility()) {
+        if (newVisibility != mMobileGroup.getVisibility() && STATE_ICON == mVisibleState) {
             mMobileGroup.setVisibility(newVisibility);
             needsLayout = true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index 9795dcf..b74140d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -16,9 +16,12 @@
 
 package com.android.systemui.statusbar.events
 
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
 import android.animation.ValueAnimator
 import android.content.Context
-import android.graphics.Point
+import android.graphics.Rect
 import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.View
@@ -30,6 +33,7 @@
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import javax.inject.Inject
+import kotlin.math.roundToInt
 
 /**
  * Controls the view for system event animations.
@@ -38,7 +42,7 @@
     private val context: Context,
     private val statusBarWindowController: StatusBarWindowController,
     private val contentInsetsProvider: StatusBarContentInsetsProvider
-) : SystemStatusChipAnimationCallback {
+) : SystemStatusAnimationCallback {
 
     private lateinit var animationWindowView: FrameLayout
 
@@ -49,90 +53,169 @@
     private var chipRight = 0
     private var chipLeft = 0
     private var chipWidth = 0
-    private var dotCenter = Point(0, 0)
+    private var chipMinWidth = context.resources.getDimensionPixelSize(
+            R.dimen.ongoing_appops_chip_min_animation_width)
     private var dotSize = context.resources.getDimensionPixelSize(
             R.dimen.ongoing_appops_dot_diameter)
-    // If the chip animates away to a persistent dot, then we modify the CHIP_OUT animation
-    private var isAnimatingToDot = false
+    // Use during animation so that multiple animators can update the drawing rect
+    private var animRect = Rect()
 
     // TODO: move to dagger
     private var initialized = false
 
-    override fun onChipAnimationStart(
-        viewCreator: ViewCreator,
-        @SystemAnimationState state: Int
-    ) {
-        if (!initialized) init()
+    /**
+     * Give the chip controller a chance to inflate and configure the chip view before we start
+     * animating
+     */
+    fun prepareChipAnimation(viewCreator: ViewCreator) {
+        if (!initialized) {
+            init()
+        }
+        animationDirection = if (animationWindowView.isLayoutRtl) RIGHT else LEFT
 
-        if (state == ANIMATING_IN) {
-            animationDirection = if (animationWindowView.isLayoutRtl) RIGHT else LEFT
+        // Initialize the animated view
+        val insets = contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
+        currentAnimatedView = viewCreator(context).also {
+            animationWindowView.addView(
+                    it.view,
+                    layoutParamsDefault(
+                            if (animationWindowView.isLayoutRtl) insets.first
+                            else insets.second))
+            it.view.alpha = 0f
+            // For some reason, the window view's measured width is always 0 here, so use the
+            // parent (status bar)
+            it.view.measure(
+                    View.MeasureSpec.makeMeasureSpec(
+                            (animationWindowView.parent as View).width, AT_MOST),
+                    View.MeasureSpec.makeMeasureSpec(animationWindowView.height, AT_MOST))
+            chipWidth = it.chipWidth
+        }
 
-            // Initialize the animated view
-            val insets = contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
-            currentAnimatedView = viewCreator(context).also {
-                animationWindowView.addView(
-                        it.view,
-                        layoutParamsDefault(
-                                if (animationWindowView.isLayoutRtl) insets.first
-                                else insets.second))
-                it.view.alpha = 0f
-                // For some reason, the window view's measured width is always 0 here, so use the
-                // parent (status bar)
-                it.view.measure(
-                        View.MeasureSpec.makeMeasureSpec(
-                                (animationWindowView.parent as View).width, AT_MOST),
-                        View.MeasureSpec.makeMeasureSpec(animationWindowView.height, AT_MOST))
-                chipWidth = it.chipWidth
+        // decide which direction we're animating from, and then set some screen coordinates
+        val contentRect = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation()
+        when (animationDirection) {
+            LEFT -> {
+                chipRight = contentRect.right
+                chipLeft = contentRect.right - chipWidth
             }
-
-            // decide which direction we're animating from, and then set some screen coordinates
-            val contentRect = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation()
-            when (animationDirection) {
-                LEFT -> {
-                    chipRight = contentRect.right
-                    chipLeft = contentRect.right - chipWidth
-                }
-                else /* RIGHT */ -> {
-                    chipLeft = contentRect.left
-                    chipRight = contentRect.left + chipWidth
-                }
-            }
-
-            currentAnimatedView?.apply {
-                updateAnimatedViewBoundsForAmount(0.1f, this)
-            }
-        } else {
-            // We are animating away
-            currentAnimatedView!!.view.apply {
-                alpha = 1f
+            else /* RIGHT */ -> {
+                chipLeft = contentRect.left
+                chipRight = contentRect.left + chipWidth
             }
         }
     }
 
-    override fun onChipAnimationEnd(@SystemAnimationState state: Int) {
-        if (state == ANIMATING_IN) {
-            // Finished animating in
-            currentAnimatedView?.apply {
-                updateAnimatedViewBoundsForAmount(1f, this)
-            }
-        } else {
-            // Finished animating away
-            currentAnimatedView!!.view.apply {
-                visibility = View.INVISIBLE
-            }
-            animationWindowView.removeView(currentAnimatedView!!.view)
+    override fun onSystemEventAnimationBegin(): Animator {
+        initializeAnimRect()
+
+        val alphaIn = ValueAnimator.ofFloat(0f, 1f).apply {
+            startDelay = 117
+            duration = 83
+            interpolator = null
+            addUpdateListener { currentAnimatedView?.view?.alpha = animatedValue as Float }
         }
+        val moveIn = ValueAnimator.ofInt(chipMinWidth, chipWidth).apply {
+            startDelay = 117
+            duration = 383
+            interpolator = STATUS_BAR_X_MOVE_IN
+            addUpdateListener {
+                updateAnimatedViewBoundsWidth(animatedValue as Int)
+            }
+        }
+        val animSet = AnimatorSet()
+        animSet.playTogether(alphaIn, moveIn)
+        return animSet
     }
 
-    override fun onChipAnimationUpdate(
-        animator: ValueAnimator,
-        @SystemAnimationState state: Int
-    ) {
-        currentAnimatedView?.apply {
-            val amt = (animator.animatedValue as Float).amt()
-            view.alpha = (animator.animatedValue as Float)
-            updateAnimatedViewBoundsForAmount(amt, this)
+    override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator {
+        initializeAnimRect()
+        val finish = if (hasPersistentDot) {
+            createMoveOutAnimationForDot()
+        } else {
+            createMoveOutAnimationDefault()
         }
+
+        finish.addListener(object : AnimatorListenerAdapter() {
+            override fun onAnimationEnd(animation: Animator?) {
+                animationWindowView.removeView(currentAnimatedView!!.view)
+            }
+        })
+
+        return finish
+    }
+
+    private fun createMoveOutAnimationForDot(): Animator {
+        val width1 = ValueAnimator.ofInt(chipWidth, chipMinWidth).apply {
+            duration = 150
+            interpolator = STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_1
+            addUpdateListener {
+                updateAnimatedViewBoundsWidth(it.animatedValue as Int)
+            }
+        }
+
+        val width2 = ValueAnimator.ofInt(chipMinWidth, dotSize).apply {
+            startDelay = 150
+            duration = 333
+            interpolator = STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_2
+            addUpdateListener {
+                updateAnimatedViewBoundsWidth(it.animatedValue as Int)
+            }
+        }
+
+        val keyFrame1Height = dotSize * 2
+        val v = currentAnimatedView!!.view
+        val chipVerticalCenter = v.top + v.measuredHeight / 2
+        val height1 = ValueAnimator.ofInt(
+                currentAnimatedView!!.view.measuredHeight, keyFrame1Height).apply {
+            startDelay = 133
+            duration = 100
+            interpolator = STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_1
+            addUpdateListener {
+                updateAnimatedViewBoundsHeight(it.animatedValue as Int, chipVerticalCenter)
+            }
+        }
+
+        val height2 = ValueAnimator.ofInt(keyFrame1Height, dotSize).apply {
+            startDelay = 233
+            duration = 250
+            interpolator = STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_2
+            addUpdateListener {
+                updateAnimatedViewBoundsHeight(it.animatedValue as Int, chipVerticalCenter)
+            }
+        }
+
+        // Move the chip view to overlap exactly with the privacy dot. The chip displays by default
+        // exactly adjacent to the dot, so we can just move over by the diameter of the dot itself
+        val moveOut = ValueAnimator.ofInt(0, dotSize).apply {
+            startDelay = 50
+            duration = 183
+            interpolator = STATUS_CHIP_MOVE_TO_DOT
+            addUpdateListener {
+                // If RTL, we can just invert the move
+                val amt = if (animationDirection == LEFT) {
+                        animatedValue as Int
+                } else {
+                    -(animatedValue as Int)
+                }
+                updateAnimatedBoundsX(amt)
+            }
+        }
+
+        val animSet = AnimatorSet()
+        animSet.playTogether(width1, width2, height1, height2, moveOut)
+        return animSet
+    }
+
+    private fun createMoveOutAnimationDefault(): Animator {
+        val moveOut = ValueAnimator.ofInt(chipWidth, chipMinWidth).apply {
+            duration = 383
+            addUpdateListener {
+                currentAnimatedView?.apply {
+                    updateAnimatedViewBoundsWidth(it.animatedValue as Int)
+                }
+            }
+        }
+        return moveOut
     }
 
     private fun init() {
@@ -144,7 +227,6 @@
         statusBarWindowController.addViewToWindow(animationWindowView, lp)
         animationWindowView.clipToPadding = false
         animationWindowView.clipChildren = false
-        animationWindowView.measureAllChildren = true
     }
 
     private fun layoutParamsDefault(marginEnd: Int): FrameLayout.LayoutParams =
@@ -153,29 +235,55 @@
                 it.marginEnd = marginEnd
             }
 
-    private fun updateAnimatedViewBoundsForAmount(amt: Float, chip: BackgroundAnimatableView) {
+    private fun initializeAnimRect() = animRect.set(
+            chipLeft,
+            currentAnimatedView!!.view.top,
+            chipRight,
+            currentAnimatedView!!.view.bottom)
+
+    /**
+     * To be called during an animation, sets the width and updates the current animated chip view
+     */
+    private fun updateAnimatedViewBoundsWidth(width: Int) {
         when (animationDirection) {
             LEFT -> {
-                chip.setBoundsForAnimation(
-                        (chipRight - (chipWidth * amt)).toInt(),
-                        chip.view.top,
-                        chipRight,
-                        chip.view.bottom)
-            }
-            else /* RIGHT */ -> {
-                chip.setBoundsForAnimation(
-                        chipLeft,
-                        chip.view.top,
-                        (chipLeft + (chipWidth * amt)).toInt(),
-                        chip.view.bottom)
+                animRect.set((chipRight - width), animRect.top, chipRight, animRect.bottom)
+            } else /* RIGHT */ -> {
+                animRect.set(chipLeft, animRect.top, (chipLeft + width), animRect.bottom)
             }
         }
+
+        updateCurrentAnimatedView()
     }
 
-    private fun start() = if (animationWindowView.isLayoutRtl) right() else left()
-    private fun right() = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation().right
-    private fun left() = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation().left
-    private fun Float.amt() = 0.01f.coerceAtLeast(this)
+    /**
+     * To be called during an animation, updates the animation rect and sends the update to the chip
+     */
+    private fun updateAnimatedViewBoundsHeight(height: Int, verticalCenter: Int) {
+        animRect.set(
+                animRect.left,
+                verticalCenter - (height.toFloat() / 2).roundToInt(),
+                animRect.right,
+                verticalCenter + (height.toFloat() / 2).roundToInt())
+
+        updateCurrentAnimatedView()
+    }
+
+    /**
+     * To be called during an animation, updates the animation rect offset and updates the chip
+     */
+    private fun updateAnimatedBoundsX(translation: Int) {
+        currentAnimatedView?.view?.translationX = translation.toFloat()
+    }
+
+    /**
+     * To be called during an animation. Sets the chip rect to animRect
+     */
+    private fun updateCurrentAnimatedView() {
+        currentAnimatedView?.setBoundsForAnimation(
+                animRect.left, animRect.top, animRect.right, animRect.bottom
+        )
+    }
 }
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 947f3eb..36233e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -19,14 +19,12 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.animation.AnimatorSet
-import android.animation.ValueAnimator
 import android.annotation.IntDef
 import android.os.Process
 import android.provider.DeviceConfig
 import android.util.Log
+import android.view.animation.PathInterpolator
 import com.android.systemui.Dumpable
-import com.android.systemui.animation.Interpolators.STANDARD_ACCELERATE
-import com.android.systemui.animation.Interpolators.STANDARD_DECELERATE
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
@@ -38,6 +36,7 @@
 import com.android.systemui.util.time.SystemClock
 import java.io.FileDescriptor
 import java.io.PrintWriter
+import java.lang.IllegalStateException
 
 import javax.inject.Inject
 
@@ -116,7 +115,10 @@
             scheduledEvent?.updateFromEvent(event)
             if (event.forceVisible) {
                 hasPersistentDot = true
-                notifyTransitionToPersistentDot()
+                // If we missed the chance to show the persistent dot, do it now
+                if (animationState == IDLE) {
+                    notifyTransitionToPersistentDot()
+                }
             }
         } else {
             if (DEBUG) {
@@ -162,68 +164,90 @@
             return
         }
 
-        // Schedule the animation to start after a debounce period
-        cancelExecutionRunnable = executor.executeDelayed({
-            cancelExecutionRunnable = null
-            animationState = ANIMATING_IN
-            statusBarWindowController.setForceStatusBarVisible(true)
+        chipAnimationController.prepareChipAnimation(scheduledEvent!!.viewCreator)
+        animationState = ANIMATION_QUEUED
+        executor.executeDelayed({
+            runChipAnimation()
+        }, DEBOUNCE_DELAY)
+    }
 
-            val entranceAnimator = ValueAnimator.ofFloat(1f, 0f)
-            entranceAnimator.duration = ENTRANCE_ANIM_LENGTH
-            entranceAnimator.addListener(systemAnimatorAdapter)
-            entranceAnimator.addUpdateListener(systemUpdateListener)
-            entranceAnimator.interpolator = STANDARD_ACCELERATE
+    /**
+     * 1. Define a total budget for the chip animation (1500ms)
+     * 2. Send out callbacks to listeners so that they can generate animations locally
+     * 3. Update the scheduler state so that clients know where we are
+     * 4. Maybe: provide scaffolding such as: dot location, margins, etc
+     * 5. Maybe: define a maximum animation length and enforce it. Probably only doable if we
+     * collect all of the animators and run them together.
+     */
+    private fun runChipAnimation() {
+        statusBarWindowController.setForceStatusBarVisible(true)
+        animationState = ANIMATING_IN
 
-            val chipAnimator = ValueAnimator.ofFloat(0f, 1f)
-            chipAnimator.duration = CHIP_ANIM_LENGTH
-            chipAnimator.addListener(
-                    ChipAnimatorAdapter(RUNNING_CHIP_ANIM, scheduledEvent!!.viewCreator))
-            chipAnimator.addUpdateListener(chipUpdateListener)
-            chipAnimator.interpolator = STANDARD_DECELERATE
+        val animSet = collectStartAnimations()
+        if (animSet.totalDuration > 500) {
+            throw IllegalStateException("System animation total length exceeds budget. " +
+                    "Expected: 500, actual: ${animSet.totalDuration}")
+        }
+        animSet.addListener(object : AnimatorListenerAdapter() {
+            override fun onAnimationEnd(animation: Animator?) {
+                animationState = RUNNING_CHIP_ANIM
+            }
+        })
+        animSet.start()
 
-            val aSet2 = AnimatorSet()
-            aSet2.playSequentially(entranceAnimator, chipAnimator)
-            aSet2.start()
-
-            executor.executeDelayed({
-                animationState = ANIMATING_OUT
-
-                val systemAnimator = ValueAnimator.ofFloat(0f, 1f)
-                systemAnimator.duration = ENTRANCE_ANIM_LENGTH
-                systemAnimator.addListener(systemAnimatorAdapter)
-                systemAnimator.addUpdateListener(systemUpdateListener)
-                systemAnimator.interpolator = STANDARD_DECELERATE
-                systemAnimator.addListener(object : AnimatorListenerAdapter() {
-                    override fun onAnimationEnd(animation: Animator?) {
-                        statusBarWindowController.setForceStatusBarVisible(false)
+        executor.executeDelayed({
+            val animSet2 = collectFinishAnimations()
+            animationState = ANIMATING_OUT
+            animSet2.addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator?) {
+                    animationState = if (hasPersistentDot) {
+                        SHOWING_PERSISTENT_DOT
+                    } else {
+                        IDLE
                     }
-                })
 
-                val chipAnimator = ValueAnimator.ofFloat(1f, 0f)
-                chipAnimator.duration = CHIP_ANIM_LENGTH
-                val endState = if (hasPersistentDot) {
-                    SHOWING_PERSISTENT_DOT
-                } else {
-                    IDLE
+                    statusBarWindowController.setForceStatusBarVisible(false)
                 }
-                chipAnimator.addListener(
-                    ChipAnimatorAdapter(endState, scheduledEvent!!.viewCreator))
-                chipAnimator.addUpdateListener(chipUpdateListener)
-                chipAnimator.interpolator = STANDARD_ACCELERATE
+            })
+            animSet2.start()
+            scheduledEvent = null
+        }, DISPLAY_LENGTH)
+    }
 
-                val aSet2 = AnimatorSet()
+    private fun collectStartAnimations(): AnimatorSet {
+        val animators = mutableListOf<Animator>()
+        listeners.forEach { listener ->
+            listener.onSystemEventAnimationBegin()?.let { anim ->
+                animators.add(anim)
+            }
+        }
+        animators.add(chipAnimationController.onSystemEventAnimationBegin())
+        val animSet = AnimatorSet().also {
+            it.playTogether(animators)
+        }
 
-                aSet2.play(chipAnimator).before(systemAnimator)
-                if (hasPersistentDot) {
-                    val dotAnim = notifyTransitionToPersistentDot()
-                    if (dotAnim != null) aSet2.playTogether(systemAnimator, dotAnim)
-                }
+        return animSet
+    }
 
-                aSet2.start()
+    private fun collectFinishAnimations(): AnimatorSet {
+        val animators = mutableListOf<Animator>()
+        listeners.forEach { listener ->
+            listener.onSystemEventAnimationFinish(hasPersistentDot)?.let { anim ->
+                animators.add(anim)
+            }
+        }
+        animators.add(chipAnimationController.onSystemEventAnimationFinish(hasPersistentDot))
+        if (hasPersistentDot) {
+            val dotAnim = notifyTransitionToPersistentDot()
+            if (dotAnim != null) {
+                animators.add(dotAnim)
+            }
+        }
+        val animSet = AnimatorSet().also {
+            it.playTogether(animators)
+        }
 
-                scheduledEvent = null
-            }, DISPLAY_LENGTH)
-        }, DELAY)
+        return animSet
     }
 
     private fun notifyTransitionToPersistentDot(): Animator? {
@@ -257,18 +281,6 @@
         return null
     }
 
-    private fun notifySystemStart() {
-        listeners.forEach { it.onSystemChromeAnimationStart() }
-    }
-
-    private fun notifySystemFinish() {
-        listeners.forEach { it.onSystemChromeAnimationEnd() }
-    }
-
-    private fun notifySystemAnimationUpdate(anim: ValueAnimator) {
-        listeners.forEach { it.onSystemChromeAnimationUpdate(anim) }
-    }
-
     override fun addCallback(listener: SystemStatusAnimationCallback) {
         Assert.isMainThread()
 
@@ -287,24 +299,6 @@
         }
     }
 
-    private val systemUpdateListener = ValueAnimator.AnimatorUpdateListener {
-        anim -> notifySystemAnimationUpdate(anim)
-    }
-
-    private val systemAnimatorAdapter = object : AnimatorListenerAdapter() {
-        override fun onAnimationEnd(p0: Animator?) {
-            notifySystemFinish()
-        }
-
-        override fun onAnimationStart(p0: Animator?) {
-            notifySystemStart()
-        }
-    }
-
-    private val chipUpdateListener = ValueAnimator.AnimatorUpdateListener {
-        anim -> chipAnimationController.onChipAnimationUpdate(anim, animationState)
-    }
-
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
         pw.println("Scheduled event: $scheduledEvent")
         pw.println("Has persistent privacy dot: $hasPersistentDot")
@@ -318,24 +312,6 @@
             }
         }
     }
-
-    inner class ChipAnimatorAdapter(
-        @SystemAnimationState val endState: Int,
-        val viewCreator: ViewCreator
-    ) : AnimatorListenerAdapter() {
-        override fun onAnimationEnd(p0: Animator?) {
-            chipAnimationController.onChipAnimationEnd(animationState)
-            animationState = if (endState == SHOWING_PERSISTENT_DOT && !hasPersistentDot) {
-                IDLE
-            } else {
-                endState
-            }
-        }
-
-        override fun onAnimationStart(p0: Animator?) {
-            chipAnimationController.onChipAnimationStart(viewCreator, animationState)
-        }
-    }
 }
 
 /**
@@ -344,16 +320,14 @@
  * create space for the chip animation to display. This means hiding the system elements in the
  * status bar and keyguard.
  *
- * TODO: the chip animation really only has one client, we can probably remove it from this
- * interface
- *
  * The value animators themselves are simple animators from 0.0 to 1.0. Listeners can apply any
  * interpolation they choose but realistically these are most likely to be simple alpha transitions
  */
 interface SystemStatusAnimationCallback {
-    @JvmDefault fun onSystemChromeAnimationUpdate(animator: ValueAnimator) {}
-    @JvmDefault fun onSystemChromeAnimationStart() {}
-    @JvmDefault fun onSystemChromeAnimationEnd() {}
+    /** Implement this method to return an [Animator] or [AnimatorSet] that presents the chip */
+    fun onSystemEventAnimationBegin(): Animator? { return null }
+    /** Implement this method to return an [Animator] or [AnimatorSet] that hides the chip */
+    fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator? { return null }
 
     // Best method name, change my mind
     @JvmDefault
@@ -363,50 +337,61 @@
     @JvmDefault fun onHidePersistentDot(): Animator? { return null }
 }
 
-interface SystemStatusChipAnimationCallback {
-    fun onChipAnimationUpdate(animator: ValueAnimator, @SystemAnimationState state: Int) {}
-
-    fun onChipAnimationStart(
-        viewCreator: ViewCreator,
-        @SystemAnimationState state: Int
-    ) {}
-
-    fun onChipAnimationEnd(@SystemAnimationState state: Int) {}
-}
-
 /**
+ * Animation state IntDef
  */
 @Retention(AnnotationRetention.SOURCE)
 @IntDef(
         value = [
-            IDLE, ANIMATING_IN, RUNNING_CHIP_ANIM, ANIMATING_OUT, SHOWING_PERSISTENT_DOT
+            IDLE,
+            ANIMATION_QUEUED,
+            ANIMATING_IN,
+            RUNNING_CHIP_ANIM,
+            ANIMATING_OUT,
+            SHOWING_PERSISTENT_DOT
         ]
 )
 annotation class SystemAnimationState
 
 /** No animation is in progress */
 const val IDLE = 0
+/** An animation is queued, and awaiting the debounce period */
+const val ANIMATION_QUEUED = 1
 /** System is animating out, and chip is animating in */
-const val ANIMATING_IN = 1
+const val ANIMATING_IN = 2
 /** Chip has animated in and is awaiting exit animation, and optionally playing its own animation */
-const val RUNNING_CHIP_ANIM = 2
+const val RUNNING_CHIP_ANIM = 3
 /** Chip is animating away and system is animating back */
-const val ANIMATING_OUT = 3
+const val ANIMATING_OUT = 4
 /** Chip has animated away, and the persistent dot is showing */
-const val SHOWING_PERSISTENT_DOT = 4
+const val SHOWING_PERSISTENT_DOT = 5
+
+/** Commonly-needed interpolators can go here */
+@JvmField val STATUS_BAR_X_MOVE_OUT = PathInterpolator(0.33f, 0f, 0f, 1f)
+@JvmField val STATUS_BAR_X_MOVE_IN = PathInterpolator(0f, 0f, 0f, 1f)
+/**
+ * Status chip animation to dot have multiple stages of motion, the _1 and _2 interpolators should
+ * be used in succession
+ */
+val STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_1 = PathInterpolator(0.44f, 0f, 0.25f, 1f)
+val STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_2 = PathInterpolator(0.3f, 0f, 0.26f, 1f)
+val STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_1 = PathInterpolator(0.4f, 0f, 0.17f, 1f)
+val STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_2 = PathInterpolator(0.3f, 0f, 0f, 1f)
+val STATUS_CHIP_MOVE_TO_DOT = PathInterpolator(0f, 0f, 0.05f, 1f)
 
 private const val TAG = "SystemStatusAnimationScheduler"
-private const val DELAY = 0L
+private const val DEBOUNCE_DELAY = 100L
 
 /**
- * The total time spent animation should be 1500ms. The entrance animation is how much time
- * we give to the system to animate system elements out of the way. Total chip animation length
- * will be equivalent to 2*chip_anim_length + display_length
+ * The total time spent on the chip animation is 1500ms, broken up into 3 sections:
+ *   - 500ms to animate the chip in (including animating system icons away)
+ *   - 500ms holding the chip on screen
+ *   - 500ms to animate the chip away (and system icons back)
+ *
+ *   So DISPLAY_LENGTH should be the sum of the first 2 phases, while the final 500ms accounts for
+ *   the actual animation
  */
-private const val ENTRANCE_ANIM_LENGTH = 250L
-private const val CHIP_ANIM_LENGTH = 250L
-// 1s + entrance time + chip anim_length
-private const val DISPLAY_LENGTH = 1500L
+private const val DISPLAY_LENGTH = 1000L
 
 private const val MIN_UPTIME: Long = 5 * 1000
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 7fbb0f1..02aa1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -25,7 +25,7 @@
     @JvmOverloads
     fun getAnimatorController(
         notification: ExpandableNotificationRow,
-        onFinishAnimationCallback: Runnable = Runnable {}
+        onFinishAnimationCallback: Runnable? = null
     ): NotificationLaunchAnimatorController {
         return NotificationLaunchAnimatorController(
             notificationShadeWindowViewController,
@@ -49,7 +49,7 @@
     private val headsUpManager: HeadsUpManagerPhone,
     private val notification: ExpandableNotificationRow,
     private val jankMonitor: InteractionJankMonitor,
-    private val onFinishAnimationCallback: Runnable
+    private val onFinishAnimationCallback: Runnable?
 ) : ActivityLaunchAnimator.Controller {
 
     companion object {
@@ -123,7 +123,7 @@
 
         if (!willAnimate) {
             removeHun(animate = true)
-            onFinishAnimationCallback.run()
+            onFinishAnimationCallback?.run()
         }
     }
 
@@ -142,7 +142,7 @@
         notificationShadeWindowViewController.setExpandAnimationRunning(false)
         notificationEntry.isExpandAnimationRunning = false
         removeHun(animate = true)
-        onFinishAnimationCallback.run()
+        onFinishAnimationCallback?.run()
     }
 
     override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
@@ -162,7 +162,7 @@
         notificationListContainer.setExpandingNotification(null)
         applyParams(null)
         removeHun(animate = false)
-        onFinishAnimationCallback.run()
+        onFinishAnimationCallback?.run()
     }
 
     private fun applyParams(params: ExpandAnimationParameters?) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index fca2aa1..577d536 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -163,10 +163,13 @@
     }
 
     /**
-     * @return The background of this view.
+     * @param width The actual width to apply to the background view.
      */
-    public NotificationBackgroundView getBackgroundNormal() {
-        return mBackgroundNormal;
+    public void setBackgroundWidth(int width) {
+        if (mBackgroundNormal == null) {
+            return;
+        }
+        mBackgroundNormal.setActualWidth(width);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index c237e1d..e479509 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -79,7 +79,6 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.internal.widget.CachingIconView;
 import com.android.internal.widget.CallLayout;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.classifier.FalsingCollector;
@@ -90,6 +89,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
 import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
@@ -111,10 +111,11 @@
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.SwipeableView;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.DumpUtilsKt;
@@ -169,6 +170,7 @@
     private RowContentBindStage mRowContentBindStage;
     private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
     private Optional<BubblesManager> mBubblesManagerOptional;
+    private MetricsLogger mMetricsLogger;
     private int mIconTransformContentShift;
     private int mMaxHeadsUpHeightBeforeN;
     private int mMaxHeadsUpHeightBeforeP;
@@ -304,8 +306,7 @@
                 final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
                 boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
                 mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
-                MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
-                        nowExpanded);
+                mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded);
                 onExpansionChanged(true /* userAction */, wasExpanded);
             } else if (mEnableNonGroupedNotificationExpand) {
                 if (v.isAccessibilityFocused()) {
@@ -327,8 +328,7 @@
                 }
                 notifyHeightChanged(true);
                 mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
-                MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_EXPANDER,
-                        nowExpanded);
+                mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_EXPANDER, nowExpanded);
             }
         }
     };
@@ -1271,7 +1271,7 @@
             addView(mMenuRow.getMenuView(), menuIndex);
         }
         for (NotificationContentView l : mLayouts) {
-            l.initView();
+            l.reinflate();
             l.reInflateViews();
         }
         mEntry.getSbn().clearPackageContext();
@@ -1464,7 +1464,7 @@
      * @param fromAccessibility whether this dismiss is coming from an accessibility action
      */
     public void performDismiss(boolean fromAccessibility) {
-        Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
+        mMetricsLogger.count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
         dismiss(fromAccessibility);
         if (mEntry.isDismissable()) {
             if (mOnUserInteractionCallback != null) {
@@ -1600,7 +1600,10 @@
             PeopleNotificationIdentifier peopleNotificationIdentifier,
             OnUserInteractionCallback onUserInteractionCallback,
             Optional<BubblesManager> bubblesManagerOptional,
-            NotificationGutsManager gutsManager) {
+            NotificationGutsManager gutsManager,
+            MetricsLogger metricsLogger,
+            SmartReplyConstants smartReplyConstants,
+            SmartReplyController smartReplyController) {
         mEntry = entry;
         mAppName = appName;
         if (mMenuRow == null) {
@@ -1623,15 +1626,18 @@
         mFalsingManager = falsingManager;
         mFalsingCollector = falsingCollector;
         mStatusBarStateController = statusBarStateController;
-
         mPeopleNotificationIdentifier = peopleNotificationIdentifier;
         for (NotificationContentView l : mLayouts) {
-            l.setPeopleNotificationIdentifier(mPeopleNotificationIdentifier);
-            l.setRemoteInputViewSubcomponentFactory(rivSubcomponentFactory);
+            l.initialize(
+                    mPeopleNotificationIdentifier,
+                    rivSubcomponentFactory,
+                    smartReplyConstants,
+                    smartReplyController);
         }
         mOnUserInteractionCallback = onUserInteractionCallback;
         mBubblesManagerOptional = bubblesManagerOptional;
         mNotificationGutsManager = gutsManager;
+        mMetricsLogger = metricsLogger;
 
         cacheIsSystemNotification();
     }
@@ -3127,7 +3133,7 @@
         if (mGroupMembershipManager.isGroupSummary(mEntry)) {
             event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
         }
-        MetricsLogger.action(mContext, event, userExpanded);
+        mMetricsLogger.action(event, userExpanded);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index c4beb5b..599039d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -27,6 +27,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
@@ -35,6 +36,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.FeedbackIcon;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
@@ -49,6 +51,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
 import com.android.systemui.util.time.SystemClock;
 import com.android.systemui.wmshell.BubblesManager;
@@ -82,6 +85,7 @@
     private final HeadsUpManager mHeadsUpManager;
     private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener;
     private final StatusBarStateController mStatusBarStateController;
+    private final MetricsLogger mMetricsLogger;
 
     private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
             this::logNotificationExpansion;
@@ -94,16 +98,21 @@
     private final boolean mAllowLongPress;
     private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
     private final Optional<BubblesManager> mBubblesManagerOptional;
+    private final SmartReplyConstants mSmartReplyConstants;
+    private final SmartReplyController mSmartReplyController;
 
     private final ExpandableNotificationRowDragController mDragController;
 
     @Inject
     public ExpandableNotificationRowController(
             ExpandableNotificationRow view,
-            NotificationListContainer listContainer,
-            RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
             ActivatableNotificationViewController activatableNotificationViewController,
+            RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
+            MetricsLogger metricsLogger,
+            NotificationListContainer listContainer,
             NotificationMediaManager mediaManager,
+            SmartReplyConstants smartReplyConstants,
+            SmartReplyController smartReplyController,
             PluginManager pluginManager,
             SystemClock clock,
             @AppName String appName,
@@ -152,6 +161,9 @@
         mPeopleNotificationIdentifier = peopleNotificationIdentifier;
         mBubblesManagerOptional = bubblesManagerOptional;
         mDragController = dragController;
+        mMetricsLogger = metricsLogger;
+        mSmartReplyConstants = smartReplyConstants;
+        mSmartReplyController = smartReplyController;
     }
 
     /**
@@ -179,7 +191,10 @@
                 mPeopleNotificationIdentifier,
                 mOnUserInteractionCallback,
                 mBubblesManagerOptional,
-                mNotificationGutsManager
+                mNotificationGutsManager,
+                mMetricsLogger,
+                mSmartReplyConstants,
+                mSmartReplyController
         );
         mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
         if (mAllowLongPress) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index adb4ce6..b9c71132 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -39,7 +39,6 @@
 import android.widget.LinearLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.RemoteInputController;
@@ -94,7 +93,6 @@
     private final Rect mClipBounds = new Rect();
 
     private int mMinContractedHeight;
-    private int mNotificationContentMarginEnd;
     private View mContractedChild;
     private View mExpandedChild;
     private View mHeadsUpChild;
@@ -116,7 +114,7 @@
     private NotificationViewWrapper mContractedWrapper;
     private NotificationViewWrapper mExpandedWrapper;
     private NotificationViewWrapper mHeadsUpWrapper;
-    private HybridGroupManager mHybridGroupManager;
+    private final HybridGroupManager mHybridGroupManager;
     private int mClipTopAmount;
     private int mContentHeight;
     private int mVisibleType = VISIBLE_TYPE_NONE;
@@ -128,7 +126,6 @@
     private int mHeadsUpHeight;
     private int mNotificationMaxHeight;
     private NotificationEntry mNotificationEntry;
-    private GroupMembershipManager mGroupMembershipManager;
     private RemoteInputController mRemoteInputController;
     private Runnable mExpandedVisibleListener;
     private PeopleNotificationIdentifier mPeopleIdentifier;
@@ -184,7 +181,6 @@
     private boolean mFocusOnVisibilityChange;
     private boolean mHeadsUpAnimatingAway;
     private int mClipBottomAmount;
-    private boolean mIsLowPriority;
     private boolean mIsContentExpandable;
     private boolean mRemoteInputVisible;
     private int mUnrestrictedContentHeight;
@@ -192,16 +188,23 @@
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mHybridGroupManager = new HybridGroupManager(getContext());
-        mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
-        mSmartReplyController = Dependency.get(SmartReplyController.class);
-        initView();
+        reinflate();
     }
 
-    public void initView() {
+    public void initialize(
+            PeopleNotificationIdentifier peopleNotificationIdentifier,
+            RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
+            SmartReplyConstants smartReplyConstants,
+            SmartReplyController smartReplyController) {
+        mPeopleIdentifier = peopleNotificationIdentifier;
+        mRemoteInputSubcomponentFactory = rivSubcomponentFactory;
+        mSmartReplyConstants = smartReplyConstants;
+        mSmartReplyController = smartReplyController;
+    }
+
+    public void reinflate() {
         mMinContractedHeight = getResources().getDimensionPixelSize(
                 R.dimen.min_notification_layout_height);
-        mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.notification_content_margin_end);
     }
 
     public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
@@ -1606,7 +1609,6 @@
     }
 
     public void setGroupMembershipManager(GroupMembershipManager groupMembershipManager) {
-        mGroupMembershipManager = groupMembershipManager;
     }
 
     public void setRemoteInputController(RemoteInputController r) {
@@ -1694,10 +1696,6 @@
         mContainingNotification = containingNotification;
     }
 
-    public void setPeopleNotificationIdentifier(PeopleNotificationIdentifier peopleIdentifier) {
-        mPeopleIdentifier = peopleIdentifier;
-    }
-
     public void requestSelectLayout(boolean needsAnimation) {
         selectLayout(needsAnimation, false);
     }
@@ -1865,7 +1863,6 @@
     }
 
     public void setIsLowPriority(boolean isLowPriority) {
-        mIsLowPriority = isLowPriority;
     }
 
     public boolean isDimmable() {
@@ -2090,10 +2087,6 @@
         return false;
     }
 
-    public void setRemoteInputViewSubcomponentFactory(RemoteInputViewSubcomponent.Factory factory) {
-        mRemoteInputSubcomponentFactory = factory;
-    }
-
     private static class RemoteInputViewData {
         @Nullable RemoteInputView mView;
         @Nullable RemoteInputViewController mController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 27cc326..ade95a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -782,6 +782,14 @@
         y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
         drawDebugInfo(canvas, y, Color.BLUE,
                 /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = "+y);
+
+        y = (int) mAmbientState.getStackY() + mContentHeight;
+        drawDebugInfo(canvas, y, Color.MAGENTA,
+                /* label= */ "mAmbientState.getStackY() + mContentHeight = " + y);
+
+        y = (int) mAmbientState.getStackY() + mIntrinsicContentHeight;
+        drawDebugInfo(canvas, y, Color.YELLOW,
+                /* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y);
     }
 
     private void drawDebugInfo(Canvas canvas, int y, int color, String label) {
@@ -1293,14 +1301,13 @@
             mOnStackYChanged.accept(listenerNeedsAnimation);
         }
         if (mQsExpansionFraction <= 0 && !shouldSkipHeightUpdate()) {
-            final float endHeight = updateStackEndHeight(
-                    getHeight(), getEmptyBottomMargin(), mTopPadding);
+            final float endHeight = updateStackEndHeight();
             updateStackHeight(endHeight, fraction);
         }
     }
 
-    public float updateStackEndHeight(float height, float bottomMargin, float topPadding) {
-        final float stackEndHeight = Math.max(0f, height - bottomMargin - topPadding);
+    private float updateStackEndHeight() {
+        final float stackEndHeight = Math.max(0f, mIntrinsicContentHeight);
         mAmbientState.setStackEndHeight(stackEndHeight);
         return stackEndHeight;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 65173a2..cb332bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -174,7 +174,7 @@
     }
 
     private void updateKeyguardStatusBarHeight() {
-        MarginLayoutParams lp =  (MarginLayoutParams) getLayoutParams();
+        MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
         lp.height = getStatusBarHeaderHeightKeyguard(mContext);
         setLayoutParams(lp);
     }
@@ -510,28 +510,6 @@
         }
     }
 
-    void onSystemChromeAnimationStart(boolean isAnimatingOut) {
-        if (isAnimatingOut) {
-            mSystemIconsContainer.setVisibility(View.VISIBLE);
-            mSystemIconsContainer.setAlpha(0f);
-        }
-    }
-
-    void onSystemChromeAnimationEnd(boolean isAnimatingIn) {
-        // Make sure the system icons are out of the way
-        if (isAnimatingIn) {
-            mSystemIconsContainer.setVisibility(View.INVISIBLE);
-            mSystemIconsContainer.setAlpha(0f);
-        } else {
-            mSystemIconsContainer.setAlpha(1f);
-            mSystemIconsContainer.setVisibility(View.VISIBLE);
-        }
-    }
-
-    void onSystemChromeAnimationUpdate(float animatedValue) {
-        mSystemIconsContainer.setAlpha(animatedValue);
-    }
-
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 1df1aff..af4fb8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -17,8 +17,6 @@
 package com.android.systemui.statusbar.phone;
 
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -47,6 +45,7 @@
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventAnimator;
 import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker;
 import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
 import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherFeatureController;
@@ -107,6 +106,8 @@
                 @Override
                 public void onDensityOrFontScaleChanged() {
                     mView.loadDimens();
+                    // The animator is dependent on resources for offsets
+                    mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, getResources());
                 }
 
                 @Override
@@ -123,21 +124,16 @@
 
     private final SystemStatusAnimationCallback mAnimationCallback =
             new SystemStatusAnimationCallback() {
+                @NonNull
                 @Override
-                public void onSystemChromeAnimationStart() {
-                    mView.onSystemChromeAnimationStart(
-                            mAnimationScheduler.getAnimationState() == ANIMATING_OUT);
+                public Animator onSystemEventAnimationFinish(boolean hasPersistentDot) {
+                    return mSystemEventAnimator.onSystemEventAnimationFinish(hasPersistentDot);
                 }
 
+                @NonNull
                 @Override
-                public void onSystemChromeAnimationEnd() {
-                    mView.onSystemChromeAnimationEnd(
-                            mAnimationScheduler.getAnimationState() == ANIMATING_IN);
-                }
-
-                @Override
-                public void onSystemChromeAnimationUpdate(@NonNull ValueAnimator anim) {
-                    mView.onSystemChromeAnimationUpdate((float) anim.getAnimatedValue());
+                public Animator onSystemEventAnimationBegin() {
+                    return mSystemEventAnimator.onSystemEventAnimationBegin();
                 }
             };
 
@@ -232,6 +228,7 @@
     private int mStatusBarState;
     private boolean mDozing;
     private boolean mShowingKeyguardHeadsUp;
+    private StatusBarSystemEventAnimator mSystemEventAnimator;
 
     @Inject
     public KeyguardStatusBarViewController(
@@ -302,6 +299,7 @@
         mView.setKeyguardUserAvatarEnabled(
                 !mFeatureController.isStatusBarUserSwitcherFeatureEnabled());
         mFeatureController.addCallback(enabled -> mView.setKeyguardUserAvatarEnabled(!enabled));
+        mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, r);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 24f5ff8..78edc07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -445,7 +445,7 @@
                 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
             }
             @Classifier.InteractionType int interactionType = vel == 0 ? GENERIC
-                    : vel > 0 ? QUICK_SETTINGS
+                    : y - mInitialTouchY > 0 ? QUICK_SETTINGS
                             : (mKeyguardStateController.canDismissLockScreen()
                                     ? UNLOCK : BOUNCER_UNLOCK);
 
@@ -532,7 +532,7 @@
             return true;
         }
 
-        @Classifier.InteractionType int interactionType = vel > 0
+        @Classifier.InteractionType int interactionType = y - mInitialTouchY > 0
                 ? QUICK_SETTINGS : (
                         mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 419661b7..1ad365b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -116,6 +116,15 @@
     private float mTransitionToFullShadeProgress;
 
     /**
+     * Same as {@link #mTransitionToFullShadeProgress}, but specifically for the notifications scrim
+     * on the lock screen.
+     *
+     * On split shade lock screen we want the different scrims to fade in at different times and
+     * rates.
+     */
+    private float mTransitionToLockScreenFullShadeNotificationsProgress;
+
+    /**
      * If we're currently transitioning to the full shade.
      */
     private boolean mTransitioningToFullShade;
@@ -574,11 +583,17 @@
      * Set the amount of progress we are currently in if we're transitioning to the full shade.
      * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
      * shade.
+     *
+     * @param progress the progress for all scrims.
+     * @param lockScreenNotificationsProgress the progress specifically for the notifications scrim.
      */
-    public void setTransitionToFullShadeProgress(float progress) {
-        if (progress != mTransitionToFullShadeProgress) {
+    public void setTransitionToFullShadeProgress(float progress,
+            float lockScreenNotificationsProgress) {
+        if (progress != mTransitionToFullShadeProgress || lockScreenNotificationsProgress
+                != mTransitionToLockScreenFullShadeNotificationsProgress) {
             mTransitionToFullShadeProgress = progress;
-            setTransitionToFullShade(progress > 0.0f);
+            mTransitionToLockScreenFullShadeNotificationsProgress = lockScreenNotificationsProgress;
+            setTransitionToFullShade(progress > 0.0f || lockScreenNotificationsProgress > 0.0f);
             applyAndDispatchState();
         }
     }
@@ -754,12 +769,13 @@
                 } else {
                     mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion);
                 }
-                if (mState == ScrimState.KEYGUARD && mTransitionToFullShadeProgress > 0.0f) {
+                if (mState == ScrimState.KEYGUARD
+                        && mTransitionToLockScreenFullShadeNotificationsProgress > 0.0f) {
                     // Interpolate the notification alpha when transitioning!
                     mNotificationsAlpha = MathUtils.lerp(
                             mNotificationsAlpha,
                             getInterpolatedFraction(),
-                            mTransitionToFullShadeProgress);
+                            mTransitionToLockScreenFullShadeNotificationsProgress);
                 }
                 mNotificationsTint = mState.getNotifTint();
                 mBehindTint = behindTint;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 637e4be..6fe92fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -397,15 +397,25 @@
             mMainThreadHandler.post(() -> {
                 final Runnable removeNotification = () -> {
                     mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK, summaryToRemove);
+                    if (!animate) {
+                        // If we're animating, this would be invoked after the activity launch
+                        // animation completes. Since we're not animating, the launch already
+                        // happened synchronously, so we notify the launch is complete here after
+                        // onDismiss.
+                        mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry);
+                    }
                 };
                 if (mPresenter.isCollapsing()) {
-                    // To avoid lags we're only performing the remove
-                    // after the shade is collapsed
+                    // To avoid lags we're only performing the remove after the shade is collapsed
                     mShadeController.addPostCollapseAction(removeNotification);
                 } else {
                     removeNotification.run();
                 }
             });
+        } else if (!canBubble && !animate) {
+            // Not animating, this is the end of the launch flow (see above comment for more info).
+            mMainThreadHandler.post(
+                    () -> mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry));
         }
 
         mIsCollapsingToShowActivityOverLockscreen = false;
@@ -481,8 +491,9 @@
             boolean isActivityIntent) {
         mLogger.logStartNotificationIntent(entry.getKey(), intent);
         try {
-            Runnable onFinishAnimationCallback =
-                    () -> mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry);
+            Runnable onFinishAnimationCallback = animate
+                    ? () -> mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry)
+                    : null;
             ActivityLaunchAnimator.Controller animationController =
                     new StatusBarLaunchAnimatorController(
                             mNotificationAnimationProvider
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 2c84219..33bc401 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -20,12 +20,10 @@
 import static android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP;
 import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
 
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
 import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
 import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT;
 
-import android.animation.ValueAnimator;
+import android.animation.Animator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -136,6 +134,7 @@
         }
     };
     private OperatorNameViewController mOperatorNameViewController;
+    private StatusBarSystemEventAnimator mSystemEventAnimator;
 
     @SuppressLint("ValidFragment")
     public CollapsedStatusBarFragment(
@@ -210,7 +209,8 @@
         initEmergencyCryptkeeperText();
         initOperatorName();
         initNotificationIconArea();
-        mAnimationScheduler.addCallback(this);
+        mSystemEventAnimator =
+                new StatusBarSystemEventAnimator(mSystemIconArea, getResources());
     }
 
     @VisibleForTesting
@@ -245,6 +245,7 @@
         mCommandQueue.addCallback(this);
         mStatusBarStateController.addCallback(this);
         initOngoingCallChip();
+        mAnimationScheduler.addCallback(this);
 
         mSecureSettings.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON),
@@ -258,6 +259,7 @@
         mCommandQueue.removeCallback(this);
         mStatusBarStateController.removeCallback(this);
         mOngoingCallController.removeCallback(mOngoingCallListener);
+        mAnimationScheduler.removeCallback(this);
         mSecureSettings.unregisterContentObserver(mVolumeSettingObserver);
     }
 
@@ -265,7 +267,6 @@
     public void onDestroyView() {
         super.onDestroyView();
         mStatusBarIconController.removeIconGroup(mDarkIconManager);
-        mAnimationScheduler.removeCallback(this);
         if (mNetworkController.hasEmergencyCryptKeeperText()) {
             mNetworkController.removeCallback(mSignalCallback);
         }
@@ -576,35 +577,16 @@
         disable(getContext().getDisplayId(), mDisabled1, mDisabled2, false /* animate */);
     }
 
+    @Nullable
     @Override
-    public void onSystemChromeAnimationStart() {
-        if (mAnimationScheduler.getAnimationState() == ANIMATING_OUT
-                && !isSystemIconAreaDisabled()) {
-            mSystemIconArea.setVisibility(View.VISIBLE);
-            mSystemIconArea.setAlpha(0f);
-        }
+    public Animator onSystemEventAnimationBegin() {
+        return mSystemEventAnimator.onSystemEventAnimationBegin();
     }
 
+    @Nullable
     @Override
-    public void onSystemChromeAnimationEnd() {
-        // Make sure the system icons are out of the way
-        if (mAnimationScheduler.getAnimationState() == ANIMATING_IN) {
-            mSystemIconArea.setVisibility(View.INVISIBLE);
-            mSystemIconArea.setAlpha(0f);
-        } else {
-            if (isSystemIconAreaDisabled()) {
-                // don't unhide
-                return;
-            }
-
-            mSystemIconArea.setAlpha(1f);
-            mSystemIconArea.setVisibility(View.VISIBLE);
-        }
-    }
-
-    @Override
-    public void onSystemChromeAnimationUpdate(@NonNull ValueAnimator animator) {
-        mSystemIconArea.setAlpha((float) animator.getAnimatedValue());
+    public Animator onSystemEventAnimationFinish(boolean hasPersistentDot) {
+        return mSystemEventAnimator.onSystemEventAnimationFinish(hasPersistentDot);
     }
 
     private boolean isSystemIconAreaDisabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
new file mode 100644
index 0000000..f530ec8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.fragment
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.content.res.Resources
+import android.view.View
+import com.android.systemui.R
+import com.android.systemui.statusbar.events.STATUS_BAR_X_MOVE_IN
+import com.android.systemui.statusbar.events.STATUS_BAR_X_MOVE_OUT
+import com.android.systemui.statusbar.events.SystemStatusAnimationCallback
+
+/**
+ * Tied directly to [SystemStatusAnimationScheduler]. Any StatusBar-like thing (keyguard, collapsed
+ * status bar fragment), can just feed this an animatable view to get the default system status
+ * animation.
+ *
+ * This animator relies on resources, and should be recreated whenever resources are updated. While
+ * this class could be used directly as the animation callback, it's probably best to forward calls
+ * to it so that it can be recreated at any moment without needing to remove/add callback.
+ */
+class StatusBarSystemEventAnimator(
+    val animatedView: View,
+    resources: Resources
+) : SystemStatusAnimationCallback {
+    private val translationXIn: Int = resources.getDimensionPixelSize(
+            R.dimen.ongoing_appops_chip_animation_in_status_bar_translation_x)
+    private val translationXOut: Int = resources.getDimensionPixelSize(
+            R.dimen.ongoing_appops_chip_animation_out_status_bar_translation_x)
+
+    override fun onSystemEventAnimationBegin(): Animator {
+        val moveOut = ValueAnimator.ofFloat(0f, 1f).setDuration(383)
+        moveOut.interpolator = STATUS_BAR_X_MOVE_OUT
+        moveOut.addUpdateListener { animation: ValueAnimator ->
+            animatedView.translationX = -(translationXIn * animation.animatedValue as Float)
+        }
+        val alphaOut = ValueAnimator.ofFloat(1f, 0f).setDuration(133)
+        alphaOut.interpolator = null
+        alphaOut.addUpdateListener { animation: ValueAnimator ->
+            animatedView.alpha = animation.animatedValue as Float
+        }
+
+        val animSet = AnimatorSet()
+        animSet.playTogether(moveOut, alphaOut)
+        return animSet
+    }
+
+    override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator {
+        animatedView.translationX = translationXOut.toFloat()
+        val moveIn = ValueAnimator.ofFloat(1f, 0f).setDuration(467)
+        moveIn.startDelay = 33
+        moveIn.interpolator = STATUS_BAR_X_MOVE_IN
+        moveIn.addUpdateListener { animation: ValueAnimator ->
+            animatedView.translationX = translationXOut * animation.animatedValue as Float
+        }
+        val alphaIn = ValueAnimator.ofFloat(0f, 1f).setDuration(167)
+        alphaIn.startDelay = 67
+        alphaIn.interpolator = null
+        alphaIn.addUpdateListener { animation: ValueAnimator ->
+            animatedView.alpha = animation.animatedValue as Float
+        }
+
+        val animatorSet = AnimatorSet()
+        animatorSet.playTogether(moveIn, alphaIn)
+
+        return animatorSet
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
index bd87875..f845101 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
@@ -299,10 +299,7 @@
         view.clearAttachment()
         entry.remoteInputUri = null
         entry.remoteInputMimeType = null
-        val resultSource = entry.editedSuggestionInfo
-                ?.let { RemoteInput.SOURCE_FREE_FORM_INPUT }
-                ?: RemoteInput.SOURCE_CHOICE
-        RemoteInput.setResultsSource(fillInIntent, resultSource)
+        RemoteInput.setResultsSource(fillInIntent, remoteInputResultsSource)
         return fillInIntent
     }
 
@@ -332,10 +329,12 @@
         entry.remoteInputText = fullText
 
         // mirror prepareRemoteInputFromText for text input
-        val resultSource = entry.editedSuggestionInfo
-                ?.let { RemoteInput.SOURCE_FREE_FORM_INPUT }
-                ?: RemoteInput.SOURCE_CHOICE
-        RemoteInput.setResultsSource(fillInIntent, resultSource)
+        RemoteInput.setResultsSource(fillInIntent, remoteInputResultsSource)
         return fillInIntent
     }
+
+    private val remoteInputResultsSource
+        get() = entry.editedSuggestionInfo
+                ?.let { RemoteInput.SOURCE_CHOICE }
+                ?: RemoteInput.SOURCE_FREE_FORM_INPUT
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/AnimationUtil.kt b/packages/SystemUI/src/com/android/systemui/util/animation/AnimationUtil.kt
new file mode 100644
index 0000000..c0538c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/AnimationUtil.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.animation
+
+import kotlin.math.roundToLong
+
+/** A generic util class for animations in SysUI. */
+class AnimationUtil {
+    companion object {
+        /**
+         * Returns the number of milliseconds there are in [numFrames] for a 60 fps device.
+         *
+         * Note that this method can be used on any device, not just 60 fps devices. Animation
+         * lengths are typically specified in terms of number of frames for a 60 fps device, and
+         * the value "5 frames" is often more meaningful than "83ms". This method allows us to
+         * write animation code in terms of the more meaningful "5" number.
+         *
+         * @param numFrames must be >= 0.
+         */
+        fun getMsForFrames(numFrames: Int): Long {
+            if (numFrames < 0) {
+                throw IllegalArgumentException("numFrames must be >= 0")
+            }
+            return (numFrames * 1000f / 60f).roundToLong()
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 7a0db1f..8c79277 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -208,6 +208,11 @@
         }
     }
 
+    /** Delegates to {@link android.testing.TestableResources#addOverride(int, Object)}. */
+    protected void overrideResource(int resourceId, Object value) {
+        mContext.getOrCreateTestableResources().addOverride(resourceId, value);
+    }
+
     public static final class EmptyRunnable implements Runnable {
         public void run() {
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index a49c4d7..c684b66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -172,11 +172,10 @@
 
     @Test
     public void enableWindowMagnification_notifySourceBoundsChanged() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
-                    Float.NaN, /* magnificationFrameOffsetRatioX= */ 0,
-                    /* magnificationFrameOffsetRatioY= */ 0, null);
-        });
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                        Float.NaN, /* magnificationFrameOffsetRatioX= */ 0,
+                /* magnificationFrameOffsetRatioY= */ 0, null));
 
         // Waits for the surface created
         verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onSourceBoundsChanged(
@@ -184,6 +183,16 @@
     }
 
     @Test
+    public void enableWindowMagnification_disabled_notifySourceBoundsChanged() {
+        enableWindowMagnification_notifySourceBoundsChanged();
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.deleteWindowMagnification(null));
+        Mockito.reset(mWindowMagnifierCallback);
+
+        enableWindowMagnification_notifySourceBoundsChanged();
+    }
+
+    @Test
     public void enableWindowMagnification_withAnimation_schedulesFrame() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(2.0f, 10,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index ec2c1de..a95da62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -114,12 +114,13 @@
     }
 
     @Test
-    fun testFingerprintTrigger_Ripple() {
+    fun testFingerprintTrigger_KeyguardVisible_Ripple() {
         // GIVEN fp exists, keyguard is visible, user doesn't need strong auth
         val fpsLocation = PointF(5f, 5f)
         `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
         controller.onViewAttached()
         `when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(true)
+        `when`(keyguardUpdateMonitor.isDreaming).thenReturn(false)
         `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
 
         // WHEN fingerprint authenticated
@@ -136,7 +137,30 @@
     }
 
     @Test
-    fun testFingerprintTrigger_KeyguardNotVisible_NoRipple() {
+    fun testFingerprintTrigger_Dreaming_Ripple() {
+        // GIVEN fp exists, keyguard is visible, user doesn't need strong auth
+        val fpsLocation = PointF(5f, 5f)
+        `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
+        controller.onViewAttached()
+        `when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(false)
+        `when`(keyguardUpdateMonitor.isDreaming).thenReturn(true)
+        `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
+
+        // WHEN fingerprint authenticated
+        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+        captor.value.onBiometricAuthenticated(
+                0 /* userId */,
+                BiometricSourceType.FINGERPRINT /* type */,
+                false /* isStrongBiometric */)
+
+        // THEN update sensor location and show ripple
+        verify(rippleView).setFingerprintSensorLocation(fpsLocation, -1f)
+        verify(rippleView).startUnlockedRipple(any())
+    }
+
+    @Test
+    fun testFingerprintTrigger_KeyguardNotVisible_NotDreaming_NoRipple() {
         // GIVEN fp exists & user doesn't need strong auth
         val fpsLocation = PointF(5f, 5f)
         `when`(authController.udfpsSensorLocation).thenReturn(fpsLocation)
@@ -145,6 +169,7 @@
 
         // WHEN keyguard is NOT visible & fingerprint authenticated
         `when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(false)
+        `when`(keyguardUpdateMonitor.isDreaming).thenReturn(false)
         val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
         verify(keyguardUpdateMonitor).registerCallback(captor.capture())
         captor.value.onBiometricAuthenticated(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
index daf81bd..dcbe0ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -37,6 +37,7 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 import javax.inject.Provider
+import org.mockito.Mockito.`when` as whenever
 
 private val DATA = MediaData(
     userId = -1,
@@ -82,6 +83,7 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
+        whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
         mediaCarouselController = MediaCarouselController(
             context,
             mediaControlPanelFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 90eff1a..cb68d81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -101,6 +101,7 @@
     @Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
     @Mock private lateinit var mediaCarouselController: MediaCarouselController
     @Mock private lateinit var falsingManager: FalsingManager
+    @Mock private lateinit var mediaFlags: MediaFlags
     private lateinit var appIcon: ImageView
     private lateinit var albumView: ImageView
     private lateinit var titleText: TextView
@@ -146,7 +147,7 @@
 
         player = MediaControlPanel(context, bgExecutor, activityStarter, broadcastSender,
             mediaViewController, seekBarViewModel, Lazy { mediaDataManager },
-            mediaOutputDialogFactory, mediaCarouselController, falsingManager, clock)
+            mediaOutputDialogFactory, mediaCarouselController, falsingManager, mediaFlags, clock)
         whenever(seekBarViewModel.progress).thenReturn(seekBarData)
 
         // Set up mock views for the players
@@ -214,6 +215,9 @@
                 device = device,
                 active = true,
                 resumeAction = null)
+
+        whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(false)
+        whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
     }
 
     /**
@@ -291,6 +295,9 @@
 
     @Test
     fun bindSemanticActionsOldLayout() {
+        whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
+        whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
+
         val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
         val semanticActions = MediaButton(
             playOrPause = MediaAction(icon, Runnable {}, "play"),
@@ -325,6 +332,9 @@
 
     @Test
     fun bindSemanticActionsNewLayout() {
+        whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
+        whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
+
         val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
         val semanticActions = MediaButton(
                 playOrPause = MediaAction(icon, Runnable {}, "play"),
@@ -371,6 +381,9 @@
 
     @Test
     fun bindNotificationActionsNewLayout() {
+        whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
+        whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
+
         val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
         val actions = listOf(
             MediaAction(icon, Runnable {}, "previous"),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index be2450d..925ae30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.statusbar.SbnBuilder
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
@@ -38,7 +37,6 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito
@@ -169,7 +167,7 @@
         whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA)
         whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf(mediaRecommendationItem))
         whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), anyInt())).thenReturn(false)
+        whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(false)
     }
 
     @After
@@ -596,7 +594,7 @@
     @Test
     fun testPlaybackActions_noState_usesNotification() {
         val desc = "Notification Action"
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), anyInt())).thenReturn(true)
+        whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
         whenever(controller.playbackState).thenReturn(null)
 
         val notifWithAction = SbnBuilder().run {
@@ -623,7 +621,7 @@
     @Test
     fun testPlaybackActions_hasPrevNext() {
         val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), anyInt())).thenReturn(true)
+        whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY or
                 PlaybackState.ACTION_SKIP_TO_PREVIOUS or
                 PlaybackState.ACTION_SKIP_TO_NEXT
@@ -671,7 +669,7 @@
     @Test
     fun testPlaybackActions_noPrevNext_usesCustom() {
         val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5")
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), anyInt())).thenReturn(true)
+        whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY
         val stateBuilder = PlaybackState.Builder()
                 .setActions(stateActions)
@@ -709,7 +707,7 @@
     @Test
     fun testPlaybackActions_reservedSpace() {
         val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), anyInt())).thenReturn(true)
+        whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY
         val stateBuilder = PlaybackState.Builder()
                 .setActions(stateActions)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index 8e201b5..203eb47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -223,6 +223,18 @@
     }
 
     @Test
+    fun calculateTransformationType_onLockSplitShade_goingToFullShade_mediaInvisible_returnsFade() {
+        enableSplitShade()
+        goToLockscreen()
+        expandQS()
+        whenever(lockHost.visible).thenReturn(false)
+        mediaHiearchyManager.setTransitionToFullShadeAmount(10000f)
+
+        val transformType = mediaHiearchyManager.calculateTransformationType()
+        assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+    }
+
+    @Test
     fun calculateTransformationType_onLockShade_inSplitShade_notExpanding_returnsFade() {
         enableSplitShade()
         goToLockscreen()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index ccce577..962d78c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -81,6 +81,9 @@
         whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(appIconFromPackageName)
         whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
         whenever(packageManager.getApplicationInfo(
+            any(), any<PackageManager.ApplicationInfoFlags>()
+        )).thenThrow(PackageManager.NameNotFoundException())
+        whenever(packageManager.getApplicationInfo(
             eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
         )).thenReturn(applicationInfo)
         context.setMockPackageManager(packageManager)
@@ -189,6 +192,28 @@
 
         verify(windowManager, never()).removeView(any())
     }
+    
+    @Test
+    fun displayChip_nullAppIconDrawableAndNullPackageName_stillHasIcon() {
+        controllerCommon.displayChip(getState())
+        val chipView = getChipView()
+
+        controllerCommon.setIcon(chipView, appPackageName = null, appIconDrawableOverride = null)
+
+        assertThat(chipView.getAppIconView().drawable).isNotNull()
+    }
+
+    @Test
+    fun displayChip_nullAppIconDrawableAndInvalidPackageName_stillHasIcon() {
+        controllerCommon.displayChip(getState())
+        val chipView = getChipView()
+
+        controllerCommon.setIcon(
+            chipView, appPackageName = "fakePackageName", appIconDrawableOverride = null
+        )
+
+        assertThat(chipView.getAppIconView().drawable).isNotNull()
+    }
 
     @Test
     fun setIcon_nullAppIconDrawable_iconIsFromPackageName() {
@@ -212,6 +237,28 @@
     }
 
     @Test
+    fun displayChip_nullAppNameAndNullPackageName_stillHasContentDescription() {
+        controllerCommon.displayChip(getState())
+        val chipView = getChipView()
+
+        controllerCommon.setIcon(chipView, appPackageName = null, appNameOverride = null)
+
+        assertThat(chipView.getAppIconView().contentDescription.toString()).isNotEmpty()
+    }
+
+    @Test
+    fun displayChip_nullAppNameAndInvalidPackageName_stillHasContentDescription() {
+        controllerCommon.displayChip(getState())
+        val chipView = getChipView()
+
+        controllerCommon.setIcon(
+            chipView, appPackageName = "fakePackageName", appNameOverride = null
+        )
+
+        assertThat(chipView.getAppIconView().contentDescription.toString()).isNotEmpty()
+    }
+
+    @Test
     fun displayChip_nullAppName_iconContentDescriptionIsFromPackageName() {
         controllerCommon.displayChip(getState())
         val chipView = getChipView()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 64a0a23..1d2a0ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.statusbar
 
+import org.mockito.Mockito.`when` as whenever
 import android.test.suitebuilder.annotation.SmallTest
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
@@ -18,11 +19,11 @@
 import com.android.systemui.statusbar.notification.stack.AmbientState
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
 import com.android.systemui.statusbar.phone.NotificationPanelViewController
 import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.policy.FakeConfigurationController
 import org.junit.After
 import org.junit.Assert.assertFalse
@@ -37,14 +38,13 @@
 import org.mockito.ArgumentMatchers.anyFloat
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
-import org.mockito.Mockito.`when` as whenever
-import org.mockito.ArgumentMatchers.eq
 
 private fun <T> anyObject(): T {
     return Mockito.anyObject<T>()
@@ -231,7 +231,7 @@
         transitionController.dragDownAmount = 10f
         verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
         verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
-        verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat())
+        verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
         verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(),
                 anyBoolean(), anyLong())
         verify(qS, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
@@ -242,7 +242,7 @@
         transitionController.dragDownAmount = 10f
         verify(nsslController).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
         verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
-        verify(scrimController).setTransitionToFullShadeProgress(anyFloat())
+        verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
         verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(),
                 anyBoolean(), anyLong())
         verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
@@ -311,6 +311,75 @@
     }
 
     @Test
+    fun setDragAmount_setsScrimProgressBasedOnScrimDistance() {
+        val distance = 10
+        context.orCreateTestableResources
+                .addOverride(R.dimen.lockscreen_shade_scrim_transition_distance, distance)
+        configurationController.notifyConfigurationChanged()
+
+        transitionController.dragDownAmount = 5f
+
+        verify(scrimController).transitionToFullShadeProgress(
+                progress = eq(0.5f),
+                lockScreenNotificationsProgress = anyFloat()
+        )
+    }
+
+    @Test
+    fun setDragAmount_setsNotificationsScrimProgressBasedOnNotificationsScrimDistanceAndDelay() {
+        val distance = 100
+        val delay = 10
+        context.orCreateTestableResources.addOverride(
+                R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
+        context.orCreateTestableResources.addOverride(
+                R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
+        configurationController.notifyConfigurationChanged()
+
+        transitionController.dragDownAmount = 20f
+
+        verify(scrimController).transitionToFullShadeProgress(
+                progress = anyFloat(),
+                lockScreenNotificationsProgress = eq(0.1f)
+        )
+    }
+
+    @Test
+    fun setDragAmount_dragAmountLessThanNotifDelayDistance_setsNotificationsScrimProgressToZero() {
+        val distance = 100
+        val delay = 50
+        context.orCreateTestableResources.addOverride(
+                R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
+        context.orCreateTestableResources.addOverride(
+                R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
+        configurationController.notifyConfigurationChanged()
+
+        transitionController.dragDownAmount = 20f
+
+        verify(scrimController).transitionToFullShadeProgress(
+                progress = anyFloat(),
+                lockScreenNotificationsProgress = eq(0f)
+        )
+    }
+
+    @Test
+    fun setDragAmount_dragAmountMoreThanTotalDistance_setsNotificationsScrimProgressToOne() {
+        val distance = 100
+        val delay = 50
+        context.orCreateTestableResources.addOverride(
+                R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
+        context.orCreateTestableResources.addOverride(
+                R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
+        configurationController.notifyConfigurationChanged()
+
+        transitionController.dragDownAmount = 999999f
+
+        verify(scrimController).transitionToFullShadeProgress(
+                progress = anyFloat(),
+                lockScreenNotificationsProgress = eq(1f)
+        )
+    }
+
+    @Test
     fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() {
         enableSplitShade()
 
@@ -328,9 +397,21 @@
     }
 
     private fun setSplitShadeEnabled(enabled: Boolean) {
-        context.getOrCreateTestableResources().addOverride(
-                R.bool.config_use_split_notification_shade, enabled
-        )
+        overrideResource(R.bool.config_use_split_notification_shade, enabled)
         configurationController.notifyConfigurationChanged()
     }
+
+    /**
+     * Wrapper around [ScrimController.transitionToFullShadeProgress] that has named parameters for
+     * clarify and easier refactoring of parameter names.
+     */
+    private fun ScrimController.transitionToFullShadeProgress(
+        progress: Float,
+        lockScreenNotificationsProgress: Float
+    ) {
+        scrimController.setTransitionToFullShadeProgress(
+                progress,
+                lockScreenNotificationsProgress
+        )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 7fc5ece..251ac7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -41,6 +41,7 @@
 import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.NotificationMessagingUtil;
 import com.android.systemui.R;
@@ -87,6 +88,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -252,13 +254,17 @@
                 .thenAnswer((Answer<ExpandableNotificationRowController>) invocation ->
                         new ExpandableNotificationRowController(
                                 viewCaptor.getValue(),
-                                mListContainer,
-                                mock(RemoteInputViewSubcomponent.Factory.class),
                                 mock(ActivatableNotificationViewController.class),
+                                mock(RemoteInputViewSubcomponent.Factory.class),
+                                mock(MetricsLogger.class),
+                                mListContainer,
                                 mNotificationMediaManager,
+                                mock(SmartReplyConstants.class),
+                                mock(SmartReplyController.class),
                                 mock(PluginManager.class),
                                 new FakeSystemClock(),
-                                "FOOBAR", "FOOBAR",
+                                "FOOBAR",
+                                "FOOBAR",
                                 mKeyguardBypassController,
                                 mGroupMembershipManager,
                                 mGroupExpansionManager,
@@ -275,8 +281,7 @@
                                 mock(FeatureFlags.class),
                                 mPeopleNotificationIdentifier,
                                 Optional.of(mock(BubblesManager.class)),
-                                mock(ExpandableNotificationRowDragController.class)
-                        ));
+                                mock(ExpandableNotificationRowDragController.class)));
 
         when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
                 .thenReturn(mNotificationRowComponentBuilder);
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 72f8f70..1ecb09b 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
@@ -44,6 +44,7 @@
 import android.view.LayoutInflater;
 import android.widget.RemoteViews;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.TestableDependency;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
@@ -54,6 +55,7 @@
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -73,6 +75,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
 import com.android.systemui.tests.R;
@@ -505,7 +508,10 @@
                 mPeopleNotificationIdentifier,
                 mOnUserInteractionCallback,
                 Optional.of(mock(BubblesManager.class)),
-                mock(NotificationGutsManager.class));
+                mock(NotificationGutsManager.class),
+                mock(MetricsLogger.class),
+                mock(SmartReplyConstants.class),
+                mock(SmartReplyController.class));
 
         row.setAboveShelfChangedListener(aboveShelf -> { });
         mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index eafcc35..56541f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -161,17 +161,6 @@
     }
 
     @Test
-    public void testUpdateStackEndHeight_forEndOfStackHeightAnimation() {
-        final float nsslHeight = 10f;
-        final float bottomMargin = 1f;
-        final float topPadding = 1f;
-
-        mStackScroller.updateStackEndHeight(nsslHeight, bottomMargin, topPadding);
-        final float stackEndHeight = nsslHeight - bottomMargin - topPadding;
-        assertTrue(mAmbientState.getStackEndHeight() == stackEndHeight);
-    }
-
-    @Test
     public void testUpdateStackHeight_withDozeAmount_whenDozeChanging() {
         final float dozeAmount = 0.5f;
         mAmbientState.setDozeAmount(dozeAmount);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
index 83eabb6..6526fab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
@@ -6,7 +6,6 @@
 import android.view.ViewGroup
 import android.view.WindowInsets
 import android.view.WindowManagerPolicyConstants
-import androidx.annotation.AnyRes
 import androidx.annotation.IdRes
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
@@ -104,10 +103,6 @@
         windowInsetsCallback = windowInsetsCallbackCaptor.value
     }
 
-    private fun overrideResource(@AnyRes id: Int, value: Any) {
-        mContext.orCreateTestableResources.addOverride(id, value)
-    }
-
     @Test
     fun testTaskbarVisibleInSplitShade() {
         enableSplitShade()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 0b25467..786a858 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.statusbar.phone.ScrimController.KEYGUARD_SCRIM_ALPHA;
 import static com.android.systemui.statusbar.phone.ScrimController.OPAQUE;
 import static com.android.systemui.statusbar.phone.ScrimController.SEMI_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.ScrimController.TRANSPARENT;
@@ -1263,19 +1262,36 @@
 
         mScrimController.setClipsQsScrim(true);
         float progress = 0.5f;
-        mScrimController.setTransitionToFullShadeProgress(progress);
+        float lsNotifProgress = 0.3f;
+        mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress);
         assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
                 mNotificationsScrim.getViewAlpha(), 0.2);
         progress = 0.0f;
-        mScrimController.setTransitionToFullShadeProgress(progress);
+        mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress);
         assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
                 mNotificationsScrim.getViewAlpha(), 0.2);
         progress = 1.0f;
-        mScrimController.setTransitionToFullShadeProgress(progress);
+        mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress);
         assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
                 mNotificationsScrim.getViewAlpha(), 0.2);
     }
 
+    @Test
+    public void notificationTransparency_followsNotificationScrimProgress() {
+        mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+        mScrimController.setRawPanelExpansionFraction(1.0f);
+        finishAnimationsImmediately();
+        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.setRawPanelExpansionFraction(1.0f);
+        finishAnimationsImmediately();
+
+        float progress = 0.5f;
+        float notifProgress = 0.3f;
+        mScrimController.setTransitionToFullShadeProgress(progress, notifProgress);
+
+        assertThat(mNotificationsScrim.getViewAlpha()).isEqualTo(notifProgress);
+    }
+
     private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
         mScrimController.setRawPanelExpansionFraction(expansion);
         finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index d48ce8c..fa867e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -447,4 +447,15 @@
         controllerCaptor.getValue().onIntentStarted(false);
         verify(listener).onFinishLaunchNotifActivity(mNotificationRow.getEntry());
     }
+
+    @Test
+    public void testNotifActivityStarterEventSourceFinishEvent_postPanelCollapse_noAnimate() {
+        NotifActivityLaunchEvents.Listener listener =
+                mock(NotifActivityLaunchEvents.Listener.class);
+        mLaunchEventsEmitter.registerListener(listener);
+        when(mCentralSurfaces.shouldAnimateLaunch(anyBoolean())).thenReturn(false);
+        mNotificationActivityStarter
+                .onNotificationClicked(mNotificationRow.getEntry().getSbn(), mNotificationRow);
+        verify(listener).onFinishLaunchNotifActivity(mNotificationRow.getEntry());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
new file mode 100644
index 0000000..92afb03
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.animation
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import java.lang.IllegalArgumentException
+
+@SmallTest
+class AnimationUtilTest : SysuiTestCase() {
+    @Test
+    fun getMsForFrames_5frames_returns83() {
+        assertThat(AnimationUtil.getMsForFrames(5)).isEqualTo(83L)
+    }
+
+    @Test
+    fun getMsForFrames_7frames_returns117() {
+        assertThat(AnimationUtil.getMsForFrames(7)).isEqualTo(117L)
+    }
+
+    @Test
+    fun getMsForFrames_30frames_returns500() {
+        assertThat(AnimationUtil.getMsForFrames(30)).isEqualTo(500L)
+    }
+
+    @Test
+    fun getMsForFrames_60frames_returns1000() {
+        assertThat(AnimationUtil.getMsForFrames(60)).isEqualTo(1000L)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun getMsForFrames_negativeFrames_throwsException() {
+        AnimationUtil.getMsForFrames(-1)
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index fa32452..ecc45eb 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -18,6 +18,7 @@
 
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
 import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
 import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
 
 import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID;
@@ -31,15 +32,20 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.CompatibilityInfo;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.hardware.display.DisplayManagerInternal;
 import android.os.Handler;
 import android.os.Message;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TypedValue;
 import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.MagnificationSpec;
 import android.view.View;
 import android.view.accessibility.MagnificationAnimationCallback;
@@ -93,6 +99,8 @@
     // Whether the following typing focus feature for magnification is enabled.
     private boolean mMagnificationFollowTypingEnabled = true;
 
+    private final DisplayManagerInternal mDisplayManagerInternal;
+
     /**
      * This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds
      * magnification information per display.
@@ -395,6 +403,18 @@
             outRegion.set(mMagnificationRegion);
         }
 
+        private DisplayMetrics getDisplayMetricsForId() {
+            final DisplayMetrics outMetrics = new DisplayMetrics();
+            final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
+            if (displayInfo != null) {
+                displayInfo.getLogicalMetrics(outMetrics,
+                        CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+            } else {
+                outMetrics.setToDefaults();
+            }
+            return outMetrics;
+        }
+
         void requestRectangleOnScreen(int left, int top, int right, int bottom) {
             synchronized (mLock) {
                 final Rect magnifiedFrame = mTempRect;
@@ -408,6 +428,12 @@
 
                 final float scrollX;
                 final float scrollY;
+                // We offset an additional distance for a user to know the surrounding context.
+                DisplayMetrics metrics = getDisplayMetricsForId();
+                final float offsetViewportX = (float) magnifFrameInScreenCoords.width() / 4;
+                final float offsetViewportY =
+                        TypedValue.applyDimension(COMPLEX_UNIT_DIP, 10, metrics);
+
                 if (right - left > magnifFrameInScreenCoords.width()) {
                     final int direction = TextUtils
                             .getLayoutDirectionFromLocale(Locale.getDefault());
@@ -417,9 +443,9 @@
                         scrollX = right - magnifFrameInScreenCoords.right;
                     }
                 } else if (left < magnifFrameInScreenCoords.left) {
-                    scrollX = left - magnifFrameInScreenCoords.left;
+                    scrollX = left - magnifFrameInScreenCoords.left - offsetViewportX;
                 } else if (right > magnifFrameInScreenCoords.right) {
-                    scrollX = right - magnifFrameInScreenCoords.right;
+                    scrollX = right - magnifFrameInScreenCoords.right + offsetViewportX;
                 } else {
                     scrollX = 0;
                 }
@@ -427,9 +453,9 @@
                 if (bottom - top > magnifFrameInScreenCoords.height()) {
                     scrollY = top - magnifFrameInScreenCoords.top;
                 } else if (top < magnifFrameInScreenCoords.top) {
-                    scrollY = top - magnifFrameInScreenCoords.top;
+                    scrollY = top - magnifFrameInScreenCoords.top - offsetViewportY;
                 } else if (bottom > magnifFrameInScreenCoords.bottom) {
-                    scrollY = bottom - magnifFrameInScreenCoords.bottom;
+                    scrollY = bottom - magnifFrameInScreenCoords.bottom + offsetViewportY;
                 } else {
                     scrollY = 0;
                 }
@@ -687,6 +713,7 @@
         mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this);
         mMagnificationInfoChangedCallback = magnificationInfoChangedCallback;
         mScaleProvider = scaleProvider;
+        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
     }
 
     /**
diff --git a/services/api/current.txt b/services/api/current.txt
index 5a28802..780fccf 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -47,6 +47,9 @@
 package com.android.server.pm {
 
   public interface PackageManagerLocal {
+    method public void reconcileSdkData(@Nullable String, @NonNull String, @NonNull java.util.List<java.lang.String>, int, int, int, @NonNull String, int) throws java.io.IOException;
+    field public static final int FLAG_STORAGE_CE = 2; // 0x2
+    field public static final int FLAG_STORAGE_DE = 1; // 0x1
   }
 
 }
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
index 116c739..8e0e395 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
@@ -107,6 +107,10 @@
     @GuardedBy("mLock")
     public void onSearchLocked(@NonNull SearchRequest searchRequest,
             @NonNull ICloudSearchManagerCallback callback) {
+        if (mRemoteComponentName == null) {
+            return;
+        }
+
         String filterList = searchRequest.getSearchConstraints().containsKey(
                 SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER)
                 ? searchRequest.getSearchConstraints().getString(
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index f32eebc..2a83a3c 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -49,7 +49,7 @@
  * The following is the list of the APIs provided by {@link CompanionApplicationController} (to be
  * utilized by {@link CompanionDeviceManagerService}):
  * <ul>
- * <li> {@link #bindCompanionApplication(int, String)}
+ * <li> {@link #bindCompanionApplication(int, String, boolean)}
  * <li> {@link #unbindCompanionApplication(int, String)}
  * <li> {@link #notifyCompanionApplicationDeviceAppeared(AssociationInfo)}
  * <li> {@link #notifyCompanionApplicationDeviceDisappeared(AssociationInfo)}
@@ -103,8 +103,12 @@
         mCompanionServicesRegister.invalidate(userId);
     }
 
-    void bindCompanionApplication(@UserIdInt int userId, @NonNull String packageName) {
-        if (DEBUG) Log.i(TAG, "bind() u" + userId + "/" + packageName);
+    void bindCompanionApplication(@UserIdInt int userId, @NonNull String packageName,
+            boolean bindImportant) {
+        if (DEBUG) {
+            Log.i(TAG, "bind() u" + userId + "/" + packageName
+                    + " important=" + bindImportant);
+        }
 
         final List<ComponentName> companionServices =
                 mCompanionServicesRegister.forPackage(userId, packageName);
@@ -125,7 +129,8 @@
             }
 
             serviceConnectors = CollectionUtils.map(companionServices, componentName ->
-                            new CompanionDeviceServiceConnector(mContext, userId, componentName));
+                            CompanionDeviceServiceConnector.newInstance(mContext, userId,
+                                    componentName, bindImportant));
             mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors);
         }
 
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index eaa99f7..13a5a28 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -254,9 +254,12 @@
 
         final int userId = association.getUserId();
         final String packageName = association.getPackageName();
+        // Set bindImportant to true when the association is self-managed to avoid the target
+        // service being killed.
+        final boolean bindImportant = association.isSelfManaged();
 
         if (!mCompanionAppController.isCompanionApplicationBound(userId, packageName)) {
-            mCompanionAppController.bindCompanionApplication(userId, packageName);
+            mCompanionAppController.bindCompanionApplication(userId, packageName, bindImportant);
         } else if (DEBUG) {
             Log.i(TAG, "u" + userId + "\\" + packageName + " is already bound");
         }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
index f2a58b7..4c7b9b8 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
@@ -16,6 +16,7 @@
 
 package com.android.server.companion;
 
+import static android.content.Context.BIND_ALMOST_PERCEPTIBLE;
 import static android.content.Context.BIND_IMPORTANT;
 import static android.os.Process.THREAD_PRIORITY_DEFAULT;
 
@@ -44,20 +45,43 @@
 class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDeviceService> {
     private static final String TAG = "CompanionDevice_ServiceConnector";
     private static final boolean DEBUG = false;
-    private static final int BINDING_FLAGS = BIND_IMPORTANT;
 
     /** Listener for changes to the state of the {@link CompanionDeviceServiceConnector}  */
     interface Listener {
         void onBindingDied(@UserIdInt int userId, @NonNull String packageName);
     }
 
-    private final @UserIdInt int mUserId;
-    private final @NonNull ComponentName mComponentName;
-    private @Nullable Listener mListener;
+    @UserIdInt
+    private final int mUserId;
+    @NonNull
+    private final ComponentName mComponentName;
+    @Nullable
+    private Listener mListener;
 
-    CompanionDeviceServiceConnector(@NonNull Context context, @UserIdInt int userId,
-            @NonNull ComponentName componentName) {
-        super(context, buildIntent(componentName), BINDING_FLAGS, userId, null);
+    /**
+     * Create a CompanionDeviceServiceConnector instance.
+     *
+     * When bindImportant is false, the binding flag will be BIND_ALMOST_PERCEPTIBLE
+     * (oom_score_adj = PERCEPTIBLE_MEDIUM_APP = 225). The target service will be treated
+     * as important as a perceptible app (IMPORTANCE_VISIBLE = 200), and will be unbound when
+     * the app is removed from task manager.
+     * When bindImportant is true, the binding flag will be BIND_IMPORTANT
+     * (oom_score_adj = PERCEPTIBLE_MEDIUM_APP = -700). The target service will
+     * have the highest priority to avoid being killed (IMPORTANCE_FOREGROUND = 100).
+     *
+     * One time permission's importance level to keep session alive is
+     * IMPORTANCE_FOREGROUND_SERVICE = 125. In order to kill the one time permission session, the
+     * service importance level should be higher than 125.
+     */
+    static CompanionDeviceServiceConnector newInstance(@NonNull Context context,
+            @UserIdInt int userId, @NonNull ComponentName componentName, boolean bindImportant) {
+        final int bindingFlags = bindImportant ? BIND_IMPORTANT : BIND_ALMOST_PERCEPTIBLE;
+        return new CompanionDeviceServiceConnector(context, userId, componentName, bindingFlags);
+    }
+
+    private CompanionDeviceServiceConnector(@NonNull Context context, @UserIdInt int userId,
+            @NonNull ComponentName componentName, int bindingFlags) {
+        super(context, buildIntent(componentName), bindingFlags, userId, null);
         mUserId = userId;
         mComponentName = componentName;
     }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 569d480..2dfe947 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -1302,7 +1302,7 @@
             pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
 
             pw.print("  mNightMode="); pw.print(mNightMode); pw.print(" (");
-            pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") ");
+            pw.print(Shell.nightModeToStr(mNightMode, mNightModeCustomType)); pw.print(") ");
             pw.print(" mOverrideOn/Off="); pw.print(mOverrideNightModeOn);
             pw.print("/"); pw.print(mOverrideNightModeOff);
 
@@ -1917,7 +1917,8 @@
         public static final String NIGHT_MODE_STR_YES = "yes";
         public static final String NIGHT_MODE_STR_NO = "no";
         public static final String NIGHT_MODE_STR_AUTO = "auto";
-        public static final String NIGHT_MODE_STR_CUSTOM = "custom";
+        public static final String NIGHT_MODE_STR_CUSTOM_SCHEDULE = "custom_schedule";
+        public static final String NIGHT_MODE_STR_CUSTOM_BEDTIME = "custom_bedtime";
         public static final String NIGHT_MODE_STR_UNKNOWN = "unknown";
         private final IUiModeManager mInterface;
 
@@ -1931,7 +1932,7 @@
             pw.println("UiModeManager service (uimode) commands:");
             pw.println("  help");
             pw.println("    Print this help text.");
-            pw.println("  night [yes|no|auto|custom]");
+            pw.println("  night [yes|no|auto|custom_schedule|custom_bedtime]");
             pw.println("    Set or read night mode.");
             pw.println("  car [yes|no]");
             pw.println("    Set or read car mode.");
@@ -2001,14 +2002,19 @@
             }
 
             final int mode = strToNightMode(modeStr);
+            final int customType = strToNightModeCustomType(modeStr);
             if (mode >= 0) {
                 mInterface.setNightMode(mode);
+                if (mode == UiModeManager.MODE_NIGHT_CUSTOM) {
+                    mInterface.setNightModeCustomType(customType);
+                }
                 printCurrentNightMode();
                 return 0;
             } else {
                 err.println("Error: mode must be '" + NIGHT_MODE_STR_YES + "', '"
                         + NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO
-                        +  "', or '" + NIGHT_MODE_STR_CUSTOM + "'");
+                        +  "', or '" + NIGHT_MODE_STR_CUSTOM_SCHEDULE + "', or '"
+                        + NIGHT_MODE_STR_CUSTOM_BEDTIME + "'");
                 return -1;
             }
         }
@@ -2016,11 +2022,12 @@
         private void printCurrentNightMode() throws RemoteException {
             final PrintWriter pw = getOutPrintWriter();
             final int currMode = mInterface.getNightMode();
-            final String currModeStr = nightModeToStr(currMode);
+            final int customType = mInterface.getNightModeCustomType();
+            final String currModeStr = nightModeToStr(currMode, customType);
             pw.println("Night mode: " + currModeStr);
         }
 
-        private static String nightModeToStr(int mode) {
+        private static String nightModeToStr(int mode, int customType) {
             switch (mode) {
                 case UiModeManager.MODE_NIGHT_YES:
                     return NIGHT_MODE_STR_YES;
@@ -2029,7 +2036,12 @@
                 case UiModeManager.MODE_NIGHT_AUTO:
                     return NIGHT_MODE_STR_AUTO;
                 case MODE_NIGHT_CUSTOM:
-                    return NIGHT_MODE_STR_CUSTOM;
+                    if (customType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE) {
+                        return NIGHT_MODE_STR_CUSTOM_SCHEDULE;
+                    }
+                    if (customType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
+                        return NIGHT_MODE_STR_CUSTOM_BEDTIME;
+                    }
                 default:
                     return NIGHT_MODE_STR_UNKNOWN;
             }
@@ -2043,13 +2055,25 @@
                     return UiModeManager.MODE_NIGHT_NO;
                 case NIGHT_MODE_STR_AUTO:
                     return UiModeManager.MODE_NIGHT_AUTO;
-                case NIGHT_MODE_STR_CUSTOM:
+                case NIGHT_MODE_STR_CUSTOM_SCHEDULE:
+                case NIGHT_MODE_STR_CUSTOM_BEDTIME:
                     return UiModeManager.MODE_NIGHT_CUSTOM;
                 default:
                     return -1;
             }
         }
 
+        private static int strToNightModeCustomType(String customTypeStr) {
+            switch (customTypeStr) {
+                case NIGHT_MODE_STR_CUSTOM_BEDTIME:
+                    return UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
+                case NIGHT_MODE_STR_CUSTOM_SCHEDULE:
+                    return UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE;
+                default:
+                    return -1;
+            }
+        }
+
         private int handleCarMode() throws RemoteException {
             final PrintWriter err = getErrPrintWriter();
             final String modeStr = getNextArg();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 48e3264..b0ab53907 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3801,6 +3801,18 @@
     @GuardedBy("mAm")
     void performScheduleRestartLocked(ServiceRecord r, @NonNull String scheduling,
             @NonNull String reason, @UptimeMillisLong long now) {
+
+        // If the service is waiting to become a foreground service, remove the pending
+        // SERVICE_FOREGROUND_TIMEOUT_MSG msg, and set fgWaiting to false, so next time the service
+        // is brought up, scheduleServiceForegroundTransitionTimeoutLocked() can be called again and
+        // a new SERVICE_FOREGROUND_TIMEOUT_MSG is scheduled in SERVICE_START_FOREGROUND_TIMEOUT
+        // again.
+        if (r.fgRequired && r.fgWaiting) {
+            mAm.mHandler.removeMessages(
+                    ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
+            r.fgWaiting = false;
+        }
+
         mAm.mHandler.removeCallbacks(r.restarter);
         mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
         r.nextRestartTime = now + r.restartDelay;
@@ -5678,7 +5690,7 @@
     void serviceForegroundTimeout(ServiceRecord r) {
         ProcessRecord app;
         synchronized (mAm) {
-            if (!r.fgRequired || r.destroying) {
+            if (!r.fgRequired || !r.fgWaiting || r.destroying) {
                 return;
             }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0735648..9d9ee8c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2911,12 +2911,35 @@
         return mAtmInternal.compatibilityInfoForPackage(ai);
     }
 
+    /**
+     * Enforces that the uid that calls a method is not an
+     * {@link UserHandle#isIsolated(int) isolated} uid.
+     *
+     * @param caller the name of the method being called.
+     * @throws SecurityException if the calling uid is an isolated uid.
+     */
     /* package */ void enforceNotIsolatedCaller(String caller) {
         if (UserHandle.isIsolated(Binder.getCallingUid())) {
             throw new SecurityException("Isolated process not allowed to call " + caller);
         }
     }
 
+    /**
+     * Enforces that the uid that calls a method is not an
+     * {@link UserHandle#isIsolated(int) isolated} uid or an
+     * {@link Process#isSdkSandboxUid(int) SDK sandbox} uid.
+     *
+     * @param caller the name of the method being called.
+     * @throws SecurityException if the calling uid is an isolated uid or SDK sandbox uid.
+     */
+    void enforceNotIsolatedOrSdkSandboxCaller(String caller) {
+        enforceNotIsolatedCaller(caller);
+
+        if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
+            throw new SecurityException("SDK sandbox process not allowed to call " + caller);
+        }
+    }
+
     @Override
     public void setPackageScreenCompatMode(String packageName, int mode) {
         mActivityTaskManager.setPackageScreenCompatMode(packageName, mode);
@@ -12843,7 +12866,7 @@
     public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
             String callerFeatureId, String receiverId, IIntentReceiver receiver,
             IntentFilter filter, String permission, int userId, int flags) {
-        enforceNotIsolatedCaller("registerReceiver");
+        enforceNotIsolatedOrSdkSandboxCaller("registerReceiver");
         ArrayList<Intent> stickyIntents = null;
         ProcessRecord callerApp = null;
         final boolean visibleToInstantApps
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 1131fa8..4fdc88d 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -561,8 +561,18 @@
             // We were asked to fetch Bluetooth data.
             final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
             if (adapter != null) {
-                bluetoothReceiver = new SynchronousResultReceiver("bluetooth");
-                adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
+                SynchronousResultReceiver resultReceiver =
+                        new SynchronousResultReceiver("bluetooth");
+                adapter.requestControllerActivityEnergyInfo(
+                        Runnable::run,
+                        info -> {
+                            Bundle bundle = new Bundle();
+                            bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY,
+                                    info);
+                            resultReceiver.send(0, bundle);
+                        }
+                );
+                bluetoothReceiver = resultReceiver;
             }
         }
 
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
index b73cf5b..42a7423 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
@@ -171,16 +171,15 @@
                 return;
             }
 
-            // Remove any existing intent and unregister for this package before adding a new one.
+            // Remove any existing PendingIntent for this package.
             String callingPackage = pendingIntent.getCreatorPackage();
             PendingIntent duplicatePendingIntent = findExistingRequestByPackage(callingPackage);
             if (duplicatePendingIntent != null) {
-                Slog.d(TAG, "Unregister duplicate request from " + callingPackage);
-                onUnregisterObserver(callingPackage);
+                Slog.d(TAG, "Replace duplicate request from " + callingPackage);
                 mExistingPendingIntents.remove(duplicatePendingIntent);
             }
 
-            // Register new package and add request to mExistingRequests
+            // Register package and add pendingIntent to mExistingPendingIntents
             startDetection(request, callingPackage, createDetectionResultRemoteCallback(),
                     getServerStatusCallback(clientStatusCallback));
             mExistingPendingIntents.add(pendingIntent);
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
index 010bf1b..e2b22dc 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
@@ -110,6 +110,8 @@
                 return runStopDetection();
             case "get-last-status-code":
                 return getLastStatusCode();
+            case "get-last-package-name":
+                return getLastPackageName();
             case "query-service-status":
                 return runQueryServiceStatus();
             case "get-bound-package":
@@ -157,6 +159,13 @@
         return lastResponse.getStatusCode();
     }
 
+    private int getLastPackageName() {
+        AmbientContextDetectionServiceStatus lastResponse =
+                sTestableCallbackInternal.getLastStatus();
+        out.println(lastResponse == null ? "" : lastResponse.getPackageName());
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -167,6 +176,7 @@
         pw.println("  start-detection USER_ID PACKAGE_NAME: Starts AmbientContextEvent detection.");
         pw.println("  stop-detection USER_ID: Stops AmbientContextEvent detection.");
         pw.println("  get-last-status-code: Prints the latest request status code.");
+        pw.println("  get-last-package-name: Prints the latest request package name.");
         pw.println("  query-event-status USER_ID PACKAGE_NAME: Prints the event status code.");
         pw.println("  get-bound-package USER_ID:"
                 + "     Print the bound package that implements the service.");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d01be58..309a4ff 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -102,6 +102,7 @@
 import android.media.IRingtonePlayer;
 import android.media.ISpatializerCallback;
 import android.media.ISpatializerHeadToSoundStagePoseCallback;
+import android.media.ISpatializerHeadTrackerAvailableCallback;
 import android.media.ISpatializerHeadTrackingModeCallback;
 import android.media.ISpatializerOutputCallback;
 import android.media.IStrategyPreferredDevicesDispatcher;
@@ -8723,6 +8724,11 @@
         return mSpatializerHelper.isHeadTrackerEnabled(Objects.requireNonNull(device));
     }
 
+    /** @see Spatializer#isHeadTrackerAvailable() */
+    public boolean isHeadTrackerAvailable() {
+        return mSpatializerHelper.isHeadTrackerAvailable();
+    }
+
     /** @see Spatializer#setSpatializerEnabled(boolean) */
     public void setSpatializerEnabled(boolean enabled) {
         enforceModifyDefaultAudioEffectsPermission();
@@ -8767,6 +8773,13 @@
         mSpatializerHelper.unregisterHeadTrackingModeCallback(cb);
     }
 
+    /** @see Spatializer.SpatializerHeadTrackerAvailableDispatcherStub */
+    public void registerSpatializerHeadTrackerAvailableCallback(
+            @NonNull ISpatializerHeadTrackerAvailableCallback cb, boolean register) {
+        Objects.requireNonNull(cb);
+        mSpatializerHelper.registerHeadTrackerAvailableCallback(cb, register);
+    }
+
     /** @see Spatializer#setOnHeadToSoundstagePoseUpdatedListener */
     public void registerHeadToSoundstagePoseCallback(
             @NonNull ISpatializerHeadToSoundStagePoseCallback cb) {
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 5af73c9..f0f04e2 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -30,6 +30,7 @@
 import android.media.ISpatializer;
 import android.media.ISpatializerCallback;
 import android.media.ISpatializerHeadToSoundStagePoseCallback;
+import android.media.ISpatializerHeadTrackerAvailableCallback;
 import android.media.ISpatializerHeadTrackingCallback;
 import android.media.ISpatializerHeadTrackingModeCallback;
 import android.media.ISpatializerOutputCallback;
@@ -126,6 +127,7 @@
     private boolean mBinauralSupported = false;
     private int mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
     private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
+    private boolean mHeadTrackerAvailable = false;
     /**
      *  The desired head tracking mode when enabling head tracking, tracks mDesiredHeadTrackingMode,
      *  except when head tracking gets disabled through setting the desired mode to
@@ -137,6 +139,7 @@
     private @Nullable SpatializerCallback mSpatCallback;
     private @Nullable SpatializerHeadTrackingCallback mSpatHeadTrackingCallback;
     private @Nullable HelperDynamicSensorCallback mDynSensorCallback;
+    private boolean mIsHeadTrackingSupported = false;
 
     // default attributes and format that determine basic availability of spatialization
     private static final AudioAttributes DEFAULT_ATTRIBUTES = new AudioAttributes.Builder()
@@ -811,8 +814,9 @@
             mSpat = AudioSystem.getSpatializer(mSpatCallback);
             try {
                 mSpat.setLevel((byte)  Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL);
+                mIsHeadTrackingSupported = mSpat.isHeadTrackingSupported();
                 //TODO: register heatracking callback only when sensors are registered
-                if (mSpat.isHeadTrackingSupported()) {
+                if (mIsHeadTrackingSupported) {
                     mSpat.registerHeadTrackingCallback(mSpatHeadTrackingCallback);
                 }
             } catch (RemoteException e) {
@@ -830,11 +834,15 @@
         if (mSpat != null) {
             mSpatCallback = null;
             try {
-                mSpat.registerHeadTrackingCallback(null);
+                if (mIsHeadTrackingSupported) {
+                    mSpat.registerHeadTrackingCallback(null);
+                }
+                mHeadTrackerAvailable = false;
                 mSpat.release();
             } catch (RemoteException e) {
                 Log.e(TAG, "Can't set release spatializer cleanly", e);
             }
+            mIsHeadTrackingSupported = false;
             mSpat = null;
         }
     }
@@ -890,6 +898,18 @@
         mHeadTrackingModeCallbacks.unregister(callback);
     }
 
+    final RemoteCallbackList<ISpatializerHeadTrackerAvailableCallback> mHeadTrackerCallbacks =
+            new RemoteCallbackList<>();
+
+    synchronized void registerHeadTrackerAvailableCallback(
+            @NonNull ISpatializerHeadTrackerAvailableCallback cb, boolean register) {
+        if (register) {
+            mHeadTrackerCallbacks.register(cb);
+        } else {
+            mHeadTrackerCallbacks.unregister(cb);
+        }
+    }
+
     synchronized int[] getSupportedHeadTrackingModes() {
         switch (mState) {
             case STATE_UNINITIALIZED:
@@ -1090,6 +1110,10 @@
         return false;
     }
 
+    synchronized boolean isHeadTrackerAvailable() {
+        return mHeadTrackerAvailable;
+    }
+
     private boolean checkSpatForHeadTracking(String funcName) {
         switch (mState) {
             case STATE_UNINITIALIZED:
@@ -1105,7 +1129,7 @@
                 }
                 break;
         }
-        return true;
+        return mIsHeadTrackingSupported;
     }
 
     private void dispatchActualHeadTrackingMode(int newMode) {
@@ -1115,7 +1139,8 @@
                 mHeadTrackingModeCallbacks.getBroadcastItem(i)
                         .dispatchSpatializerActualHeadTrackingModeChanged(newMode);
             } catch (RemoteException e) {
-                Log.e(TAG, "Error in dispatchSpatializerActualHeadTrackingModeChanged", e);
+                Log.e(TAG, "Error in dispatchSpatializerActualHeadTrackingModeChanged("
+                        + newMode + ")", e);
             }
         }
         mHeadTrackingModeCallbacks.finishBroadcast();
@@ -1128,12 +1153,27 @@
                 mHeadTrackingModeCallbacks.getBroadcastItem(i)
                         .dispatchSpatializerDesiredHeadTrackingModeChanged(newMode);
             } catch (RemoteException e) {
-                Log.e(TAG, "Error in dispatchSpatializerDesiredHeadTrackingModeChanged", e);
+                Log.e(TAG, "Error in dispatchSpatializerDesiredHeadTrackingModeChanged("
+                        + newMode + ")", e);
             }
         }
         mHeadTrackingModeCallbacks.finishBroadcast();
     }
 
+    private void dispatchHeadTrackerAvailable(boolean available) {
+        final int nbCallbacks = mHeadTrackerCallbacks.beginBroadcast();
+        for (int i = 0; i < nbCallbacks; i++) {
+            try {
+                mHeadTrackerCallbacks.getBroadcastItem(i)
+                        .dispatchSpatializerHeadTrackerAvailable(available);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in dispatchSpatializerHeadTrackerAvailable("
+                        + available + ")", e);
+            }
+        }
+        mHeadTrackerCallbacks.finishBroadcast();
+    }
+
     //------------------------------------------------------
     // head pose
     final RemoteCallbackList<ISpatializerHeadToSoundStagePoseCallback> mHeadPoseCallbacks =
@@ -1279,13 +1319,8 @@
             Log.e(TAG, "not " + action + " sensors, null spatializer");
             return;
         }
-        try {
-            if (!mSpat.isHeadTrackingSupported()) {
-                Log.e(TAG, "not " + action + " sensors, spatializer doesn't support headtracking");
-                return;
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "not " + action + " sensors, error querying headtracking", e);
+        if (!mIsHeadTrackingSupported) {
+            Log.e(TAG, "not " + action + " sensors, spatializer doesn't support headtracking");
             return;
         }
         int headHandle = -1;
@@ -1348,6 +1383,10 @@
         try {
             Log.i(TAG, "setHeadSensor:" + headHandle);
             mSpat.setHeadSensor(headHandle);
+            if (mHeadTrackerAvailable != (headHandle != -1)) {
+                mHeadTrackerAvailable = (headHandle != -1);
+                dispatchHeadTrackerAvailable(mHeadTrackerAvailable);
+            }
         } catch (Exception e) {
             Log.e(TAG, "Error calling setHeadSensor:" + headHandle, e);
         }
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index fadcce9..954b930 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -20,6 +20,7 @@
 
 import android.Manifest;
 import android.accounts.Account;
+import android.accounts.AccountManagerInternal;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -28,7 +29,10 @@
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.compat.CompatChanges;
 import android.app.job.JobInfo;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -106,6 +110,13 @@
      */
     private static final long BACKGROUND_OBSERVER_DELAY = 10 * DateUtils.SECOND_IN_MILLIS;
 
+    /**
+     * Enables checking for account access for the calling uid on all sync-related APIs.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.S_V2)
+    public static final long ACCOUNT_ACCESS_CHECK_CHANGE_ID = 201794303L;
+
     public static class Lifecycle extends SystemService {
         private ContentService mService;
 
@@ -157,6 +168,8 @@
     private SyncManager mSyncManager = null;
     private final Object mSyncManagerLock = new Object();
 
+    private final AccountManagerInternal mAccountManagerInternal;
+
     private static final BinderDeathDispatcher<IContentObserver> sObserverDeathDispatcher =
             new BinderDeathDispatcher<>();
 
@@ -317,6 +330,8 @@
         localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
         mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
                 localeFilter, null, null);
+
+        mAccountManagerInternal = LocalServices.getService(AccountManagerInternal.class);
     }
 
     void onBootPhase(int phase) {
@@ -593,6 +608,10 @@
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
 
+        if (!hasAccountAccess(true, account, callingUid)) {
+            return;
+        }
+
         validateExtras(callingUid, extras);
         final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(callingUid, extras);
 
@@ -642,11 +661,14 @@
     @Override
     public void syncAsUser(SyncRequest request, int userId, String callingPackage) {
         enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
+
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
+        if (!hasAccountAccess(true, request.getAccount(), callingUid)) {
+            return;
+        }
 
         final Bundle extras = request.getBundle();
-
         validateExtras(callingUid, extras);
         final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(callingUid, extras);
 
@@ -853,6 +875,9 @@
                 "no permission to read the sync settings for user " + userId);
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
+        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
+            return false;
+        }
 
         final long identityToken = clearCallingIdentity();
         try {
@@ -882,8 +907,13 @@
                 "no permission to write the sync settings");
         enforceCrossUserPermission(userId,
                 "no permission to modify the sync settings for user " + userId);
+
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
+        if (!hasAccountAccess(true, account, callingUid)) {
+            return;
+        }
+
         final int syncExemptionFlag = getSyncExemptionForCaller(callingUid);
 
         final long identityToken = clearCallingIdentity();
@@ -912,7 +942,11 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                 "no permission to write the sync settings");
 
-        validateExtras(Binder.getCallingUid(), extras);
+        final int callingUid = Binder.getCallingUid();
+        if (!hasAccountAccess(true, account, callingUid)) {
+            return;
+        }
+        validateExtras(callingUid, extras);
 
         int userId = UserHandle.getCallingUserId();
 
@@ -942,9 +976,11 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                 "no permission to write the sync settings");
 
-        validateExtras(Binder.getCallingUid(), extras);
-
         final int callingUid = Binder.getCallingUid();
+        if (!hasAccountAccess(true, account, callingUid)) {
+            return;
+        }
+        validateExtras(callingUid, extras);
 
         int userId = UserHandle.getCallingUserId();
         final long identityToken = clearCallingIdentity();
@@ -969,6 +1005,9 @@
         }
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
+        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
+            return new ArrayList<>(); // return a new empty list for consistent behavior
+        }
 
         int userId = UserHandle.getCallingUserId();
         final long identityToken = clearCallingIdentity();
@@ -995,6 +1034,9 @@
                 "no permission to read the sync settings for user " + userId);
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
+        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
+            return SyncStorageEngine.AuthorityInfo.NOT_SYNCABLE; // to keep behavior consistent
+        }
 
         final long identityToken = clearCallingIdentity();
         try {
@@ -1031,6 +1073,9 @@
         syncable = normalizeSyncable(syncable);
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
+        if (!hasAccountAccess(true, account, callingUid)) {
+            return;
+        }
 
         final long identityToken = clearCallingIdentity();
         try {
@@ -1103,6 +1148,10 @@
     public boolean isSyncActive(Account account, String authority, ComponentName cname) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
+        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
+            return false;
+        }
+
         int userId = UserHandle.getCallingUserId();
         final long identityToken = clearCallingIdentity();
         try {
@@ -1165,6 +1214,9 @@
                 "no permission to read the sync stats for user " + userId);
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
+        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
+            return null;
+        }
 
         final long identityToken = clearCallingIdentity();
         try {
@@ -1196,6 +1248,10 @@
                 "no permission to read the sync stats");
         enforceCrossUserPermission(userId,
                 "no permission to retrieve the sync settings for user " + userId);
+        if (!hasAccountAccess(true, account, Binder.getCallingUid())) {
+            return false;
+        }
+
         final long identityToken = clearCallingIdentity();
         SyncManager syncManager = getSyncManager();
         if (syncManager == null) return false;
@@ -1405,6 +1461,32 @@
                 Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
     }
 
+    /**
+     * Checks to see if the given account is accessible by the provided uid.
+     *
+     * @param checkCompatFlag whether to check if the ACCOUNT_ACCESS_CHECK_CHANGE_ID flag is enabled
+     * @param account the account trying to be accessed
+     * @param uid the uid trying to access the account
+     * @return {@code true} if the account is accessible by the given uid, {@code false} otherwise
+     */
+    private boolean hasAccountAccess(boolean checkCompatFlag, Account account, int uid) {
+        if (account == null) {
+            // If the account is null, it means to check for all accounts hence skip the check here.
+            return true;
+        }
+        if (checkCompatFlag
+                && !CompatChanges.isChangeEnabled(ACCOUNT_ACCESS_CHECK_CHANGE_ID, uid)) {
+            return true;
+        }
+
+        final long identityToken = clearCallingIdentity();
+        try {
+            return mAccountManagerInternal.hasAccountAccess(account, uid);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
     private static int normalizeSyncable(int syncable) {
         if (syncable > 0) {
             return SyncStorageEngine.AuthorityInfo.SYNCABLE;
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 7cb2921..cb04ddf 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -156,9 +156,15 @@
 
         mMode = mode;
 
+        DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
+        if (displayInfo == null) {
+            // displayInfo can be null if the associated display has been removed. There
+            // is a delay between the display being removed and ColorFade being dismissed.
+            return false;
+        }
+
         // Get the display size and layer stack.
         // This is not expected to change while the color fade surface is showing.
-        DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
         mDisplayLayerStack = displayInfo.layerStack;
         mDisplayWidth = displayInfo.getNaturalWidth();
         mDisplayHeight = displayInfo.getNaturalHeight();
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7a0cf4b..540ae81 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -73,6 +73,8 @@
 
     private final SurfaceControlProxy mSurfaceControlProxy;
 
+    private final boolean mIsBootDisplayModeSupported;
+
     // Called with SyncRoot lock held.
     public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
             Context context, Handler handler, Listener listener) {
@@ -85,6 +87,7 @@
         super(syncRoot, context, handler, listener, TAG);
         mInjector = injector;
         mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
+        mIsBootDisplayModeSupported = mSurfaceControlProxy.getBootDisplayModeSupport();
     }
 
     @Override
@@ -349,8 +352,7 @@
 
                 if (preferredRecord != null) {
                     int preferredModeId = preferredRecord.mMode.getModeId();
-                    if (mSurfaceControlProxy.getBootDisplayModeSupport()
-                            && mSystemPreferredModeId != preferredModeId) {
+                    if (mIsBootDisplayModeSupported && mSystemPreferredModeId != preferredModeId) {
                         mSystemPreferredModeId = preferredModeId;
                         preferredModeChanged = true;
                     }
@@ -900,7 +902,7 @@
             }
             updateDeviceInfoLocked();
 
-            if (!mSurfaceControlProxy.getBootDisplayModeSupport()) {
+            if (!mIsBootDisplayModeSupported) {
                 return;
             }
             if (mUserPreferredModeId == INVALID_MODE_ID) {
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 22d32a6..50f5536 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -467,7 +467,9 @@
         }
         mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
         mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
-        mAtmInternal.notifyDreamStateChanged(false);
+        mHandler.post(() -> {
+            mAtmInternal.notifyDreamStateChanged(false);
+        });
     }
 
     private void checkPermission(String permission) {
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 176c08c..924db6a 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -22,12 +22,14 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ILocaleManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.HandlerThread;
 import android.os.LocaleList;
@@ -154,6 +156,12 @@
         }
 
         @Override
+        @NonNull
+        public LocaleList getSystemLocales() throws RemoteException {
+            return LocaleManagerService.this.getSystemLocales();
+        }
+
+        @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out,
                 FileDescriptor err, String[] args, ShellCallback callback,
                 ResultReceiver resultReceiver) {
@@ -426,6 +434,32 @@
         return null;
     }
 
+    /**
+     * Returns the current system locales.
+     */
+    @NonNull
+    public LocaleList getSystemLocales() throws RemoteException {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return getSystemLocalesUnchecked();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @NonNull
+    private LocaleList getSystemLocalesUnchecked() throws RemoteException {
+        LocaleList systemLocales = null;
+        Configuration conf = ActivityManager.getService().getConfiguration();
+        if (conf != null) {
+            systemLocales = conf.getLocales();
+        }
+        if (systemLocales == null) {
+            systemLocales = LocaleList.getEmptyLocaleList();
+        }
+        return systemLocales;
+    }
+
     private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) {
         FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED,
                 atomRecordForMetrics.mCallingUid,
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 3ce8e46..1937852 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -45,6 +45,7 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Slog;
+import android.window.WindowContainerToken;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
@@ -410,6 +411,7 @@
         private IBinder mToken;
         private IBinder.DeathRecipient mDeathEater;
         private boolean mRestoreSystemAlertWindow;
+        private WindowContainerToken mTaskRecordingWindowContainerToken = null;
 
         MediaProjection(int type, int uid, String packageName, int targetSdkVersion,
                 boolean isPrivileged) {
@@ -568,7 +570,7 @@
             }
         }
 
-        @Override
+        @Override // Binder call
         public void registerCallback(IMediaProjectionCallback callback) {
             if (callback == null) {
                 throw new IllegalArgumentException("callback must not be null");
@@ -576,7 +578,7 @@
             mCallbackDelegate.add(callback);
         }
 
-        @Override
+        @Override // Binder call
         public void unregisterCallback(IMediaProjectionCallback callback) {
             if (callback == null) {
                 throw new IllegalArgumentException("callback must not be null");
@@ -584,6 +586,17 @@
             mCallbackDelegate.remove(callback);
         }
 
+        @Override // Binder call
+        public void setTaskRecordingWindowContainerToken(WindowContainerToken token) {
+            // TODO(b/221417940) set the task id to record from sysui, for the package chosen.
+            mTaskRecordingWindowContainerToken = token;
+        }
+
+        @Override // Binder call
+        public WindowContainerToken getTaskRecordingWindowContainerToken() {
+            return mTaskRecordingWindowContainerToken;
+        }
+
         public MediaProjectionInfo getProjectionInfo() {
             return new MediaProjectionInfo(packageName, userHandle);
         }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 9f573c2..57ffba7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -4094,7 +4094,7 @@
                     "updateNetworkStats: " + uid + "/" + (uidForeground ? "F" : "B"));
         }
         try {
-            mNetworkStats.setUidForeground(uid, uidForeground);
+            mNetworkStats.noteUidForeground(uid, uidForeground);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
         }
@@ -5421,6 +5421,11 @@
         try {
             mNetworkManager.setUidOnMeteredNetworkDenylist(uid, enable);
             mLogger.meteredAllowlistChanged(uid, enable);
+            if (Process.isApplicationUid(uid)) {
+                final int sdkSandboxUid = Process.toSdkSandboxUid(uid);
+                mNetworkManager.setUidOnMeteredNetworkDenylist(sdkSandboxUid, enable);
+                mLogger.meteredAllowlistChanged(sdkSandboxUid, enable);
+            }
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem setting denylist (" + enable + ") rules for " + uid, e);
         } catch (RemoteException e) {
@@ -5433,6 +5438,11 @@
         try {
             mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, enable);
             mLogger.meteredDenylistChanged(uid, enable);
+            if (Process.isApplicationUid(uid)) {
+                final int sdkSandboxUid = Process.toSdkSandboxUid(uid);
+                mNetworkManager.setUidOnMeteredNetworkAllowlist(sdkSandboxUid, enable);
+                mLogger.meteredDenylistChanged(sdkSandboxUid, enable);
+            }
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem setting allowlist (" + enable + ") rules for " + uid, e);
         } catch (RemoteException e) {
@@ -5471,12 +5481,31 @@
         }
     }
 
+    private void addSdkSandboxUidsIfNeeded(SparseIntArray uidRules) {
+        final int size = uidRules.size();
+        final SparseIntArray sdkSandboxUids = new SparseIntArray();
+        for (int index = 0; index < size; index++) {
+            final int uid = uidRules.keyAt(index);
+            final int rule = uidRules.valueAt(index);
+            if (Process.isApplicationUid(uid)) {
+                sdkSandboxUids.put(Process.toSdkSandboxUid(uid), rule);
+            }
+        }
+
+        for (int index = 0; index < sdkSandboxUids.size(); index++) {
+            final int uid = sdkSandboxUids.keyAt(index);
+            final int rule = sdkSandboxUids.valueAt(index);
+            uidRules.put(uid, rule);
+        }
+    }
+
     /**
      * Set uid rules on a particular firewall chain. This is going to synchronize the rules given
      * here to netd.  It will clean up dead rules and make sure the target chain only contains rules
      * specified here.
      */
     private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) {
+        addSdkSandboxUidsIfNeeded(uidRules);
         try {
             int size = uidRules.size();
             int[] uids = new int[size];
@@ -5519,6 +5548,11 @@
             try {
                 mNetworkManager.setFirewallUidRule(chain, uid, rule);
                 mLogger.uidFirewallRuleChanged(chain, uid, rule);
+                if (Process.isApplicationUid(uid)) {
+                    final int sdkSandboxUid = Process.toSdkSandboxUid(uid);
+                    mNetworkManager.setFirewallUidRule(chain, sdkSandboxUid, rule);
+                    mLogger.uidFirewallRuleChanged(chain, sdkSandboxUid, rule);
+                }
             } catch (IllegalStateException e) {
                 Log.wtf(TAG, "problem setting firewall uid rules", e);
             } catch (RemoteException e) {
@@ -5555,15 +5589,16 @@
      */
     private void resetUidFirewallRules(int uid) {
         try {
-            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT);
-            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
-            mNetworkManager
-                    .setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid, FIREWALL_RULE_DEFAULT);
-            mNetworkManager
-                    .setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid, FIREWALL_RULE_DEFAULT);
-            mNetworkManager
-                    .setFirewallUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid,
-                            FIREWALL_RULE_DEFAULT);
+            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid,
+                    FIREWALL_RULE_DEFAULT);
+            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid,
+                    FIREWALL_RULE_DEFAULT);
+            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid,
+                    FIREWALL_RULE_DEFAULT);
+            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid,
+                    FIREWALL_RULE_DEFAULT);
+            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid,
+                    FIREWALL_RULE_DEFAULT);
             mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false);
             mLogger.meteredAllowlistChanged(uid, false);
             mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false);
@@ -5573,6 +5608,9 @@
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
+        if (Process.isApplicationUid(uid)) {
+            resetUidFirewallRules(Process.toSdkSandboxUid(uid));
+        }
     }
 
     @Deprecated
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 93f1b47..9e0c975 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -104,6 +104,7 @@
 
     // The amount of time rules instances can exist without their owning app being installed.
     private static final int RULE_INSTANCE_GRACE_PERIOD = 1000 * 60 * 60 * 72;
+    static final int RULE_LIMIT_PER_PACKAGE = 100;
 
     // pkg|userId => uid
     protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
@@ -329,10 +330,10 @@
             int newRuleInstanceCount = getCurrentInstanceCount(automaticZenRule.getOwner())
                     + getCurrentInstanceCount(automaticZenRule.getConfigurationActivity())
                     + 1;
-            if (ruleInstanceLimit > 0 && ruleInstanceLimit < newRuleInstanceCount) {
+            if (newRuleInstanceCount > RULE_LIMIT_PER_PACKAGE
+                    || (ruleInstanceLimit > 0 && ruleInstanceLimit < newRuleInstanceCount)) {
                 throw new IllegalArgumentException("Rule instance limit exceeded");
             }
-
         }
 
         ZenModeConfig newConfig;
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 9ff4aab..d26a1ac 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -253,7 +253,8 @@
      *
      * <p>This is only for shell command and only root or shell user can use this.
      *
-     * @param packageNames dex optimize the passed packages or all packages if null
+     * @param packageNames dex optimize the passed packages in the given order, or all packages in
+     *         the default order if null
      *
      * @return true if dex optimization is complete. false if the task is cancelled or if there was
      *         an error.
@@ -268,11 +269,11 @@
                 resetStatesForNewDexOptRunLocked(Thread.currentThread());
             }
             PackageManagerService pm = mInjector.getPackageManagerService();
-            ArraySet<String> packagesToOptimize;
+            List<String> packagesToOptimize;
             if (packageNames == null) {
                 packagesToOptimize = mDexOptHelper.getOptimizablePackages(pm.snapshotComputer());
             } else {
-                packagesToOptimize = new ArraySet<>(packageNames);
+                packagesToOptimize = packageNames;
             }
             return runIdleOptimization(pm, packagesToOptimize, /* isPostBootUpdate= */ false);
         } finally {
@@ -335,7 +336,7 @@
             return false;
         }
 
-        ArraySet<String> pkgs = mDexOptHelper.getOptimizablePackages(pm.snapshotComputer());
+        List<String> pkgs = mDexOptHelper.getOptimizablePackages(pm.snapshotComputer());
         if (pkgs.isEmpty()) {
             Slog.i(TAG, "No packages to optimize");
             markPostBootUpdateCompleted(params);
@@ -525,7 +526,7 @@
     }
 
     /** Returns true if completed */
-    private boolean runIdleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
+    private boolean runIdleOptimization(PackageManagerService pm, List<String> pkgs,
             boolean isPostBootUpdate) {
         synchronized (mLock) {
             mLastExecutionStartTimeMs = SystemClock.elapsedRealtime();
@@ -581,10 +582,9 @@
     }
 
     @Status
-    private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
+    private int idleOptimizePackages(PackageManagerService pm, List<String> pkgs,
             long lowStorageThreshold, boolean isPostBootUpdate) {
         ArraySet<String> updatedPackages = new ArraySet<>();
-        ArraySet<String> updatedPackagesDueToSecondaryDex = new ArraySet<>();
 
         try {
             boolean supportSecondaryDex = mInjector.supportSecondaryDex();
@@ -640,25 +640,12 @@
                         }
                     }
 
-                    pkgs = new ArraySet<>(pkgs);
+                    pkgs = new ArrayList<>(pkgs);
                     pkgs.removeAll(unusedPackages);
                 }
             }
 
-            @Status int primaryResult = optimizePackages(pkgs, lowStorageThreshold,
-                    /*isForPrimaryDex=*/ true, updatedPackages, isPostBootUpdate);
-            if (primaryResult != STATUS_OK) {
-                return primaryResult;
-            }
-
-            if (!supportSecondaryDex) {
-                return STATUS_OK;
-            }
-
-            @Status int secondaryResult = optimizePackages(pkgs, lowStorageThreshold,
-                    /*isForPrimaryDex*/ false, updatedPackagesDueToSecondaryDex,
-                    isPostBootUpdate);
-            return secondaryResult;
+            return optimizePackages(pkgs, lowStorageThreshold, updatedPackages, isPostBootUpdate);
         } finally {
             // Always let the pinner service know about changes.
             notifyPinService(updatedPackages);
@@ -670,8 +657,10 @@
     }
 
     @Status
-    private int optimizePackages(ArraySet<String> pkgs, long lowStorageThreshold,
-            boolean isForPrimaryDex, ArraySet<String> updatedPackages, boolean isPostBootUpdate) {
+    private int optimizePackages(List<String> pkgs, long lowStorageThreshold,
+            ArraySet<String> updatedPackages, boolean isPostBootUpdate) {
+        boolean supportSecondaryDex = mInjector.supportSecondaryDex();
+
         for (String pkg : pkgs) {
             int abortCode = abortIdleOptimizations(lowStorageThreshold);
             if (abortCode != STATUS_OK) {
@@ -679,11 +668,23 @@
                 return abortCode;
             }
 
-            @DexOptResult int result = optimizePackage(pkg, isForPrimaryDex, isPostBootUpdate);
-            if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
+            @DexOptResult int primaryResult =
+                    optimizePackage(pkg, true /* isForPrimaryDex */, isPostBootUpdate);
+            if (primaryResult == PackageDexOptimizer.DEX_OPT_PERFORMED) {
                 updatedPackages.add(pkg);
-            } else if (result != PackageDexOptimizer.DEX_OPT_SKIPPED) {
-                return convertPackageDexOptimizerStatusToInternal(result);
+            } else if (primaryResult != PackageDexOptimizer.DEX_OPT_SKIPPED) {
+                return convertPackageDexOptimizerStatusToInternal(primaryResult);
+            }
+
+            if (!supportSecondaryDex) {
+                continue;
+            }
+
+            @DexOptResult int secondaryResult =
+                    optimizePackage(pkg, false /* isForPrimaryDex */, isPostBootUpdate);
+            if (secondaryResult != PackageDexOptimizer.DEX_OPT_PERFORMED
+                    && secondaryResult != PackageDexOptimizer.DEX_OPT_SKIPPED) {
+                return convertPackageDexOptimizerStatusToInternal(secondaryResult);
             }
         }
         return STATUS_OK;
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 50b2e23..bb2ba5c 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -293,8 +293,8 @@
         MetricsLogger.histogram(mPm.mContext, "opt_dialog_time_s", elapsedTimeSeconds);
     }
 
-    public ArraySet<String> getOptimizablePackages(@NonNull Computer snapshot) {
-        ArraySet<String> pkgs = new ArraySet<>();
+    public List<String> getOptimizablePackages(@NonNull Computer snapshot) {
+        ArrayList<String> pkgs = new ArrayList<>();
         mPm.forEachPackageState(snapshot, packageState -> {
             final AndroidPackage pkg = packageState.getPkg();
             if (pkg != null && mPm.mPackageDexOptimizer.canOptimizePackage(pkg)) {
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
similarity index 65%
rename from services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
rename to services/core/java/com/android/server/pm/InitAppsHelper.java
index 91750de..15f26e7 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -32,6 +32,7 @@
 import static com.android.server.pm.PackageManagerService.TAG;
 import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_CHECK_MAX_SDK_VERSION;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.os.Environment;
@@ -61,14 +62,24 @@
  * further cleanup and eventually all the installation/scanning related logic will go to another
  * class.
  */
-final class InitAndSystemPackageHelper {
+final class InitAppsHelper {
     private final PackageManagerService mPm;
-
     private final List<ScanPartition> mDirsToScanAsSystem;
     private final int mScanFlags;
     private final int mSystemParseFlags;
     private final int mSystemScanFlags;
     private final InstallPackageHelper mInstallPackageHelper;
+    private final ApexManager mApexManager;
+    private final ExecutorService mExecutorService;
+    /* Tracks how long system scan took */
+    private long mSystemScanTime;
+    /* Track of the number of cached system apps */
+    private int mCachedSystemApps;
+    /* Track of the number of system apps */
+    private int mSystemPackagesCount;
+    private final boolean mIsDeviceUpgrading;
+    private final boolean mIsOnlyCoreApps;
+    private final List<ScanPartition> mSystemPartitions;
 
     /**
      * Tracks new system packages [received in an OTA] that we expect to
@@ -76,21 +87,33 @@
      * are package location.
      */
     private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
+    /* Tracks of any system packages that no longer exist that needs to be pruned. */
+    private final List<String> mPossiblyDeletedUpdatedSystemApps = new ArrayList<>();
+    // Tracks of stub packages that must either be replaced with full versions in the /data
+    // partition or be disabled.
+    private final List<String> mStubSystemApps = new ArrayList<>();
 
     // TODO(b/198166813): remove PMS dependency
-    InitAndSystemPackageHelper(PackageManagerService pm) {
+    InitAppsHelper(PackageManagerService pm, ApexManager apexManager,
+            InstallPackageHelper installPackageHelper,
+            List<ScanPartition> systemPartitions) {
         mPm = pm;
-        mInstallPackageHelper = new InstallPackageHelper(pm);
+        mApexManager = apexManager;
+        mInstallPackageHelper = installPackageHelper;
+        mSystemPartitions = systemPartitions;
         mDirsToScanAsSystem = getSystemScanPartitions();
+        mIsDeviceUpgrading = mPm.isDeviceUpgrading();
+        mIsOnlyCoreApps = mPm.isOnlyCoreApps();
         // Set flag to monitor and not change apk file paths when scanning install directories.
         int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
-        if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()) {
+        if (mIsDeviceUpgrading || mPm.isFirstBoot()) {
             mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
         } else {
             mScanFlags = scanFlags;
         }
         mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
         mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM;
+        mExecutorService = ParallelPackageParser.makeExecutorService();
     }
 
     private List<File> getFrameworkResApkSplitFiles() {
@@ -118,7 +141,7 @@
 
     private List<ScanPartition> getSystemScanPartitions() {
         final List<ScanPartition> scanPartitions = new ArrayList<>();
-        scanPartitions.addAll(mPm.mInjector.getSystemPartitions());
+        scanPartitions.addAll(mSystemPartitions);
         scanPartitions.addAll(getApexScanPartitions());
         Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions);
         return scanPartitions;
@@ -126,8 +149,7 @@
 
     private List<ScanPartition> getApexScanPartitions() {
         final List<ScanPartition> scanPartitions = new ArrayList<>();
-        final List<ApexManager.ActiveApexInfo> activeApexInfos =
-                mPm.mApexManager.getActiveApexInfos();
+        final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos();
         for (int i = 0; i < activeApexInfos.size(); i++) {
             final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
             if (scanPartition != null) {
@@ -144,117 +166,134 @@
             if (apexInfo.preInstalledApexPath.getAbsolutePath().equals(
                     sp.getFolder().getAbsolutePath())
                     || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
-                        sp.getFolder().getAbsolutePath() + File.separator)) {
+                    sp.getFolder().getAbsolutePath() + File.separator)) {
                 return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
             }
         }
         return null;
     }
 
-    public OverlayConfig initPackages(
-            WatchedArrayMap<String, PackageSetting> packageSettings, int[] userIds,
-            long startTime) {
-        PackageParser2 packageParser = mPm.mInjector.getScanningCachingPackageParser();
-
-        ExecutorService executorService = ParallelPackageParser.makeExecutorService();
+    /**
+     * Install apps from system dirs.
+     */
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    public OverlayConfig initSystemApps(PackageParser2 packageParser,
+            WatchedArrayMap<String, PackageSetting> packageSettings,
+            int[] userIds, long startTime) {
         // Prepare apex package info before scanning APKs, this information is needed when
         // scanning apk in apex.
-        mPm.mApexManager.scanApexPackagesTraced(packageParser, executorService);
+        mApexManager.scanApexPackagesTraced(packageParser, mExecutorService);
 
-        scanSystemDirs(packageParser, executorService);
+        scanSystemDirs(packageParser, mExecutorService);
         // Parse overlay configuration files to set default enable state, mutability, and
         // priority of system overlays.
         final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
-        for (ApexManager.ActiveApexInfo apexInfo : mPm.mApexManager.getActiveApexInfos()) {
-            for (String packageName : mPm.mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+        for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
+            for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
                 apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
             }
         }
-        OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
+        final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
                 consumer -> mPm.forEachPackage(mPm.snapshotComputer(),
                         pkg -> consumer.accept(pkg, pkg.isSystem(),
-                          apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
-        // Prune any system packages that no longer exist.
-        final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
-        // Stub packages must either be replaced with full versions in the /data
-        // partition or be disabled.
-        final List<String> stubSystemApps = new ArrayList<>();
+                                apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
 
-        if (!mPm.isOnlyCoreApps()) {
+        if (!mIsOnlyCoreApps) {
             // do this first before mucking with mPackages for the "expecting better" case
-            updateStubSystemAppsList(stubSystemApps);
+            updateStubSystemAppsList(mStubSystemApps);
             mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings,
-                    possiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
+                    mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
         }
 
-        final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
+        logSystemAppsScanningTime(startTime);
+        return overlayConfig;
+    }
+
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    private void logSystemAppsScanningTime(long startTime) {
+        mCachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
 
         // Remove any shared userIDs that have no associated packages
         mPm.mSettings.pruneSharedUsersLPw();
-        final long systemScanTime = SystemClock.uptimeMillis() - startTime;
-        final int systemPackagesCount = mPm.mPackages.size();
-        Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
-                + " ms, packageCount: " + systemPackagesCount
+        mSystemScanTime = SystemClock.uptimeMillis() - startTime;
+        mSystemPackagesCount = mPm.mPackages.size();
+        Slog.i(TAG, "Finished scanning system apps. Time: " + mSystemScanTime
+                + " ms, packageCount: " + mSystemPackagesCount
                 + " , timePerPackage: "
-                + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
-                + " , cached: " + cachedSystemApps);
-        if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) {
+                + (mSystemPackagesCount == 0 ? 0 : mSystemScanTime / mSystemPackagesCount)
+                + " , cached: " + mCachedSystemApps);
+        if (mIsDeviceUpgrading && mSystemPackagesCount > 0) {
             //CHECKSTYLE:OFF IndentationCheck
             FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
                     BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
-                    systemScanTime / systemPackagesCount);
+                    mSystemScanTime / mSystemPackagesCount);
             //CHECKSTYLE:ON IndentationCheck
         }
+    }
 
-        if (!mPm.isOnlyCoreApps()) {
+    /**
+     * Install apps/updates from data dir and fix system apps that are affected.
+     */
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    public void initNonSystemApps(PackageParser2 packageParser, @NonNull int[] userIds,
+            long startTime) {
+        if (!mIsOnlyCoreApps) {
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                     SystemClock.uptimeMillis());
             scanDirTracedLI(mPm.getAppInstallDir(), /* frameworkSplits= */ null, 0,
-                    mScanFlags | SCAN_REQUIRE_KNOWN, 0,
-                    packageParser, executorService);
-
+                    mScanFlags | SCAN_REQUIRE_KNOWN,
+                    packageParser, mExecutorService);
         }
 
-        List<Runnable> unfinishedTasks = executorService.shutdownNow();
+        List<Runnable> unfinishedTasks = mExecutorService.shutdownNow();
         if (!unfinishedTasks.isEmpty()) {
             throw new IllegalStateException("Not all tasks finished before calling close: "
                     + unfinishedTasks);
         }
-
-        if (!mPm.isOnlyCoreApps()) {
-            mInstallPackageHelper.cleanupDisabledPackageSettings(possiblyDeletedUpdatedSystemApps,
-                    userIds, mScanFlags);
-            mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
-                    stubSystemApps, mSystemScanFlags, mSystemParseFlags);
-
-            // Uncompress and install any stubbed system applications.
-            // This must be done last to ensure all stubs are replaced or disabled.
-            mInstallPackageHelper.installSystemStubPackages(stubSystemApps, mScanFlags);
-
-            final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
-                    - cachedSystemApps;
-
-            final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
-            final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount;
-            Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
-                    + " ms, packageCount: " + dataPackagesCount
-                    + " , timePerPackage: "
-                    + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
-                    + " , cached: " + cachedNonSystemApps);
-            if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) {
-                //CHECKSTYLE:OFF IndentationCheck
-                FrameworkStatsLog.write(
-                        FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
-                        BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
-                        dataScanTime / dataPackagesCount);
-                //CHECKSTYLE:OFF IndentationCheck
-            }
+        if (!mIsOnlyCoreApps) {
+            fixSystemPackages(userIds);
+            logNonSystemAppScanningTime(startTime);
         }
         mExpectingBetter.clear();
-
         mPm.mSettings.pruneRenamedPackagesLPw();
-        packageParser.close();
-        return overlayConfig;
+    }
+
+    /**
+     * Clean up system packages now that some system package updates have been installed from
+     * the data dir. Also install system stub packages as the last step.
+     */
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    private void fixSystemPackages(@NonNull int[] userIds) {
+        mInstallPackageHelper.cleanupDisabledPackageSettings(mPossiblyDeletedUpdatedSystemApps,
+                userIds, mScanFlags);
+        mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
+                mStubSystemApps, mSystemScanFlags, mSystemParseFlags);
+
+        // Uncompress and install any stubbed system applications.
+        // This must be done last to ensure all stubs are replaced or disabled.
+        mInstallPackageHelper.installSystemStubPackages(mStubSystemApps, mScanFlags);
+    }
+
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    private void logNonSystemAppScanningTime(long startTime) {
+        final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
+                - mCachedSystemApps;
+
+        final long dataScanTime = SystemClock.uptimeMillis() - mSystemScanTime - startTime;
+        final int dataPackagesCount = mPm.mPackages.size() - mSystemPackagesCount;
+        Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+                + " ms, packageCount: " + dataPackagesCount
+                + " , timePerPackage: "
+                + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+                + " , cached: " + cachedNonSystemApps);
+        if (mIsDeviceUpgrading && dataPackagesCount > 0) {
+            //CHECKSTYLE:OFF IndentationCheck
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+                    BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
+                    dataScanTime / dataPackagesCount);
+            //CHECKSTYLE:OFF IndentationCheck
+        }
     }
 
     /**
@@ -274,13 +313,13 @@
                 continue;
             }
             scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null,
-                    mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0,
+                    mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
                     packageParser, executorService);
         }
 
         scanDirTracedLI(frameworkDir, null,
                 mSystemParseFlags,
-                mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
+                mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
                 packageParser, executorService);
         if (!mPm.mPackages.containsKey("android")) {
             throw new IllegalStateException(
@@ -292,11 +331,11 @@
             if (partition.getPrivAppFolder() != null) {
                 scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null,
                         mSystemParseFlags,
-                        mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
+                        mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
                         packageParser, executorService);
             }
             scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null,
-                    mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0,
+                    mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
                     packageParser, executorService);
         }
     }
@@ -315,7 +354,7 @@
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
     private void scanDirTracedLI(File scanDir, List<File> frameworkSplits,
             int parseFlags, int scanFlags,
-            long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
+            PackageParser2 packageParser, ExecutorService executorService) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
         try {
             if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
@@ -323,7 +362,7 @@
                 parseFlags |= PARSE_CHECK_MAX_SDK_VERSION;
             }
             mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
-                    scanFlags, currentTime, packageParser, executorService);
+                    scanFlags, packageParser, executorService);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index b39b24f..870a11a 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3062,7 +3062,7 @@
         final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
         removePackageHelper.removePackageLI(stubPkg, true /*chatty*/);
         try {
-            return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
+            return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, null);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
                     e);
@@ -3194,7 +3194,7 @@
                         | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
         @PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath);
         final AndroidPackage pkg = scanSystemPackageTracedLI(
-                        codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
+                codePath, parseFlags, scanFlags, null);
 
         PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
 
@@ -3368,7 +3368,7 @@
                 mRemovePackageHelper.removePackageLI(pkg, true);
                 try {
                     final File codePath = new File(pkg.getPath());
-                    scanSystemPackageTracedLI(codePath, 0, scanFlags, 0, null);
+                    scanSystemPackageTracedLI(codePath, 0, scanFlags, null);
                 } catch (PackageManagerException e) {
                     Slog.e(TAG, "Failed to parse updated, ex-system package: "
                             + e.getMessage());
@@ -3389,7 +3389,7 @@
 
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
     public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags,
-            int scanFlags, long currentTime, PackageParser2 packageParser,
+            int scanFlags, PackageParser2 packageParser,
             ExecutorService executorService) {
         final File[] files = scanDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
@@ -3432,7 +3432,7 @@
                             parseResult.parsedPackage);
                 }
                 try {
-                    addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, currentTime,
+                    addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
                             null);
                 } catch (PackageManagerException e) {
                     errorCode = e.error;
@@ -3495,7 +3495,7 @@
 
             try {
                 final AndroidPackage newPkg = scanSystemPackageTracedLI(
-                        scanFile, reparseFlags, rescanFlags, 0, null);
+                        scanFile, reparseFlags, rescanFlags, null);
                 // We rescanned a stub, add it to the list of stubbed system packages
                 if (newPkg.isStub()) {
                     stubSystemApps.add(packageName);
@@ -3509,14 +3509,14 @@
 
     /**
      *  Traces a package scan.
-     *  @see #scanSystemPackageLI(File, int, int, long, UserHandle)
+     *  @see #scanSystemPackageLI(File, int, int, UserHandle)
      */
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
     public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags,
-            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
+            int scanFlags, UserHandle user) throws PackageManagerException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
         try {
-            return scanSystemPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
+            return scanSystemPackageLI(scanFile, parseFlags, scanFlags, user);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -3528,7 +3528,7 @@
      */
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
     private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags,
-            long currentTime, UserHandle user) throws PackageManagerException {
+            UserHandle user) throws PackageManagerException {
         if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
@@ -3544,7 +3544,7 @@
             PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
         }
 
-        return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
+        return addForInitLI(parsedPackage, parseFlags, scanFlags, user);
     }
 
     /**
@@ -3563,11 +3563,11 @@
     @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
     private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
             @ParsingPackageUtils.ParseFlags int parseFlags,
-            @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+            @PackageManagerService.ScanFlags int scanFlags,
             @Nullable UserHandle user) throws PackageManagerException {
 
         final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI(
-                parsedPackage, parseFlags, scanFlags, currentTime, user);
+                parsedPackage, parseFlags, scanFlags, user);
         final ScanResult scanResult = scanResultPair.first;
         boolean shouldHideSystemApp = scanResultPair.second;
         if (scanResult.mSuccess) {
@@ -3762,7 +3762,7 @@
 
     private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
             @ParsingPackageUtils.ParseFlags int parseFlags,
-            @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+            @PackageManagerService.ScanFlags int scanFlags,
             @Nullable UserHandle user) throws PackageManagerException {
         final boolean scanSystemPartition =
                 (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
@@ -3950,7 +3950,7 @@
         }
 
         final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
-                scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
+                scanFlags | SCAN_UPDATE_SIGNATURE, 0 /* currentTime */, user, null);
         return new Pair<>(scanResult, shouldHideSystemApp);
     }
 
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 76b9830..5902f48 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -27,6 +27,7 @@
 import android.os.CreateAppDataResult;
 import android.os.IBinder;
 import android.os.IInstalld;
+import android.os.ReconcileSdkDataArgs;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.storage.CrateMetadata;
@@ -215,6 +216,21 @@
         return result;
     }
 
+    static ReconcileSdkDataArgs buildReconcileSdkDataArgs(String uuid, String packageName,
+            List<String> subDirNames, int userId, int appId,
+            String seInfo, int flags) {
+        final ReconcileSdkDataArgs args = new ReconcileSdkDataArgs();
+        args.uuid = uuid;
+        args.packageName = packageName;
+        args.subDirNames = subDirNames;
+        args.userId = userId;
+        args.appId = appId;
+        args.previousAppId = 0;
+        args.seInfo = seInfo;
+        args.flags = flags;
+        return args;
+    }
+
     public @NonNull CreateAppDataResult createAppData(@NonNull CreateAppDataArgs args)
             throws InstallerException {
         if (!checkBeforeRemote()) {
@@ -247,6 +263,18 @@
         }
     }
 
+    void reconcileSdkData(@NonNull ReconcileSdkDataArgs args)
+            throws InstallerException {
+        if (!checkBeforeRemote()) {
+            return;
+        }
+        try {
+            mInstalld.reconcileSdkData(args);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
     /**
      * Class that collects multiple {@code installd} operations together in an
      * attempt to more efficiently execute them in bulk.
diff --git a/services/core/java/com/android/server/pm/PackageManagerLocal.java b/services/core/java/com/android/server/pm/PackageManagerLocal.java
index 7b76567..39cc37e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerLocal.java
+++ b/services/core/java/com/android/server/pm/PackageManagerLocal.java
@@ -16,8 +16,16 @@
 
 package com.android.server.pm;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
 /**
  * In-process API for server side PackageManager related infrastructure.
  *
@@ -28,4 +36,48 @@
  */
 @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public interface PackageManagerLocal {
+
+    /**
+     * Indicates if operation should include device encrypted storage.
+     */
+    int FLAG_STORAGE_DE = Installer.FLAG_STORAGE_DE;
+    /**
+     * Indicates if operation should include credential encrypted storage.
+     */
+    int FLAG_STORAGE_CE = Installer.FLAG_STORAGE_CE;
+
+    /**
+     * Constants for use with {@link #reconcileSdkData} to specify which storage areas should be
+     * included for operation.
+     *
+     * @hide
+     */
+    @IntDef(prefix = "FLAG_STORAGE_",  value = {
+            FLAG_STORAGE_DE,
+            FLAG_STORAGE_CE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StorageFlags {}
+
+    /**
+     * Reconcile sdk data sub-directories for the given {@code packagName}.
+     *
+     * Sub directories are created if they do not exist already. If there is an existing per-
+     * sdk directory that is missing from {@code subDirNames}, then it is removed.
+     *
+     * Sdk package path is created if it doesn't exist before creating per-sdk directories.
+     *
+     * @param volumeUuid the volume in which the sdk data should be prepared.
+     * @param packageName package name of the app for which sdk data directory will be prepared.
+     * @param subDirNames names of sub directories that should be reconciled against.
+     * @param userId id of the user to whom the package belongs to.
+     * @param appId id of the package.
+     * @param previousAppId previous id of the package if package is being updated.
+     * @param flags flags from StorageManager to indicate which storage areas should be included.
+     * @param seInfo seInfo tag to be used for selinux policy.
+     * @throws IOException If any error occurs during the operation.
+     */
+    void reconcileSdkData(@Nullable String volumeUuid, @NonNull String packageName,
+            @NonNull List<String> subDirNames, int userId, int appId, int previousAppId,
+            @NonNull String seInfo, @StorageFlags int flags) throws IOException;
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 67056ea..b1b05be 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -132,6 +132,7 @@
 import android.os.ParcelableException;
 import android.os.PersistableBundle;
 import android.os.Process;
+import android.os.ReconcileSdkDataArgs;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -228,7 +229,6 @@
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationService;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
-import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
 import com.android.server.sdksandbox.SdkSandboxManagerLocal;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.utils.SnapshotCache;
@@ -935,7 +935,7 @@
     private final BroadcastHelper mBroadcastHelper;
     private final RemovePackageHelper mRemovePackageHelper;
     private final DeletePackageHelper mDeletePackageHelper;
-    private final InitAndSystemPackageHelper mInitAndSystemPackageHelper;
+    private final InitAppsHelper mInitAppsHelper;
     private final AppDataHelper mAppDataHelper;
     private final InstallPackageHelper mInstallPackageHelper;
     private final PreferredActivityHelper mPreferredActivityHelper;
@@ -1670,7 +1670,7 @@
         mAppDataHelper = testParams.appDataHelper;
         mInstallPackageHelper = testParams.installPackageHelper;
         mRemovePackageHelper = testParams.removePackageHelper;
-        mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper;
+        mInitAppsHelper = testParams.initAndSystemPackageHelper;
         mDeletePackageHelper = testParams.deletePackageHelper;
         mPreferredActivityHelper = testParams.preferredActivityHelper;
         mResolveIntentHelper = testParams.resolveIntentHelper;
@@ -1820,7 +1820,8 @@
         mAppDataHelper = new AppDataHelper(this);
         mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
         mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
-        mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this);
+        mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,
+                mInjector.getSystemPartitions());
         mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
                 mAppDataHelper);
         mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
@@ -1955,8 +1956,11 @@
                     mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
 
             final int[] userIds = mUserManager.getUserIds();
-            mOverlayConfig = mInitAndSystemPackageHelper.initPackages(packageSettings,
-                    userIds, startTime);
+            PackageParser2 packageParser = mInjector.getScanningCachingPackageParser();
+            mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,
+                    startTime);
+            mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime);
+            packageParser.close();
 
             // Resolve the storage manager.
             mStorageManagerPackage = getStorageManagerPackageName(computer);
@@ -6032,6 +6036,22 @@
     }
 
     private class PackageManagerLocalImpl implements PackageManagerLocal {
+        @Override
+        public void reconcileSdkData(@Nullable String volumeUuid, @NonNull String packageName,
+                @NonNull List<String> subDirNames, int userId, int appId, int previousAppId,
+                @NonNull String seInfo, int flags) throws IOException {
+            synchronized (mInstallLock) {
+                ReconcileSdkDataArgs args = mInstaller.buildReconcileSdkDataArgs(volumeUuid,
+                        packageName, subDirNames, userId, appId, seInfo,
+                        flags);
+                args.previousAppId = previousAppId;
+                try {
+                    mInstaller.reconcileSdkData(args);
+                } catch (InstallerException e) {
+                    throw new IOException(e.getMessage());
+                }
+            }
+        }
     }
 
     private class PackageManagerInternalImpl extends PackageManagerInternalBase {
@@ -7013,7 +7033,7 @@
     }
 
     boolean isExpectingBetter(String packageName) {
-        return mInitAndSystemPackageHelper.isExpectingBetter(packageName);
+        return mInitAppsHelper.isExpectingBetter(packageName);
     }
 
     int getDefParseFlags() {
@@ -7112,13 +7132,12 @@
     }
 
     boolean isOverlayMutable(String packageName) {
-        return (mOverlayConfig != null ? mOverlayConfig
-                : OverlayConfig.getSystemInstance()).isMutable(packageName);
+        return mOverlayConfig.isMutable(packageName);
     }
 
     @ScanFlags int getSystemPackageScanFlags(File codePath) {
         List<ScanPartition> dirsToScanAsSystem =
-                mInitAndSystemPackageHelper.getDirsToScanAsSystem();
+                mInitAppsHelper.getDirsToScanAsSystem();
         @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM;
         for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
             ScanPartition partition = dirsToScanAsSystem.get(i);
@@ -7136,7 +7155,7 @@
     Pair<Integer, Integer> getSystemPackageRescanFlagsAndReparseFlags(File scanFile,
             int systemScanFlags, int systemParseFlags) {
         List<ScanPartition> dirsToScanAsSystem =
-                mInitAndSystemPackageHelper.getDirsToScanAsSystem();
+                mInitAppsHelper.getDirsToScanAsSystem();
         @ParsingPackageUtils.ParseFlags int reparseFlags = 0;
         @PackageManagerService.ScanFlags int rescanFlags = 0;
         for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 5bdda0b..e466fe2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -109,7 +109,7 @@
     public AppDataHelper appDataHelper;
     public InstallPackageHelper installPackageHelper;
     public RemovePackageHelper removePackageHelper;
-    public InitAndSystemPackageHelper initAndSystemPackageHelper;
+    public InitAppsHelper initAndSystemPackageHelper;
     public DeletePackageHelper deletePackageHelper;
     public PreferredActivityHelper preferredActivityHelper;
     public ResolveIntentHelper resolveIntentHelper;
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index df19d3e..a991ed3 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -151,7 +151,7 @@
                 final AndroidPackage pkg;
                 try {
                     pkg = installPackageHelper.scanSystemPackageTracedLI(
-                            ps.getPath(), parseFlags, SCAN_INITIAL, 0, null);
+                            ps.getPath(), parseFlags, SCAN_INITIAL, null);
                     loaded.add(pkg);
 
                 } catch (PackageManagerException e) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 34b7ad4..70053bd 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -5409,6 +5409,21 @@
         (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
     }
 
+    private static final String PREFIX_HELP_COMMAND = "  ";
+    private static final String PREFIX_HELP_DESCRIPTION = "    ";
+    private static final String PREFIX_HELP_DESCRIPTION_EXTRA_LINES = "      ";
+
+    private static final String CMD_HELP = "help";
+    private static final String CMD_LIST = "list";
+    private static final String CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS =
+            "report-system-user-package-whitelist-problems";
+
+    private static final String ARG_V = "-v";
+    private static final String ARG_VERBOSE = "--verbose";
+    private static final String ARG_ALL = "--all";
+    private static final String ARG_CRITICAL_ONLY = "--critical-only";
+    private static final String ARG_MODE = "--mode";
+
     private int onShellCommand(Shell shell, String cmd) {
         if (cmd == null) {
             return shell.handleDefaultCommands(cmd);
@@ -5417,9 +5432,9 @@
         final PrintWriter pw = shell.getOutPrintWriter();
         try {
             switch(cmd) {
-                case "list":
+                case CMD_LIST:
                     return runList(pw, shell);
-                case "report-system-user-package-whitelist-problems":
+                case CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS:
                     return runReportPackageWhitelistProblems(pw, shell);
                 default:
                     return shell.handleDefaultCommands(cmd);
@@ -5436,10 +5451,10 @@
         String opt;
         while ((opt = shell.getNextOption()) != null) {
             switch (opt) {
-                case "-v":
+                case ARG_V:
                     verbose = true;
                     break;
-                case "--all":
+                case ARG_ALL:
                     all = true;
                     break;
                 default:
@@ -5520,14 +5535,14 @@
         String opt;
         while ((opt = shell.getNextOption()) != null) {
             switch (opt) {
-                case "-v":
-                case "--verbose":
+                case ARG_V:
+                case ARG_VERBOSE:
                     verbose = true;
                     break;
-                case "--critical-only":
+                case ARG_CRITICAL_ONLY:
                     criticalOnly = true;
                     break;
-                case "--mode":
+                case ARG_MODE:
                     mode = Integer.parseInt(shell.getNextArgRequired());
                     break;
                 default:
@@ -6248,20 +6263,28 @@
         @Override
         public void onHelp() {
             final PrintWriter pw = getOutPrintWriter();
-            pw.println("User manager (user) commands:");
-            pw.println("  help");
-            pw.println("    Prints this help text.");
-            pw.println("");
-            pw.println("  list [-v] [-all]");
-            pw.println("    Prints all users on the system.");
-            pw.println("  report-system-user-package-whitelist-problems [-v | --verbose] "
-                    + "[--critical-only] [--mode MODE]");
-            pw.println("    Reports all issues on user-type package whitelist XML files. Options:");
-            pw.println("    -v | --verbose : shows extra info, like number of issues");
-            pw.println("    --critical-only: show only critical issues, excluding warnings");
-            pw.println("    --mode MODE: shows what errors would be if device used mode MODE (where"
-                    + " MODE is the whitelist mode integer as defined by "
-                    + "config_userTypePackageWhitelistMode)");
+            pw.printf("User manager (user) commands:\n");
+
+            pw.printf("%s%s\n", PREFIX_HELP_COMMAND, CMD_HELP);
+            pw.printf("%sPrints this help text.\n\n", PREFIX_HELP_DESCRIPTION);
+
+            pw.printf("%s%s [%s] [%s]\n", PREFIX_HELP_COMMAND, CMD_LIST, ARG_V, ARG_ALL);
+            pw.printf("%sPrints all users on the system.\n\n", PREFIX_HELP_DESCRIPTION);
+
+            pw.printf("%s%s [%s | %s] [%s] [%s MODE]\n", PREFIX_HELP_COMMAND,
+                    CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS,
+                    ARG_V, ARG_VERBOSE, ARG_CRITICAL_ONLY, ARG_MODE);
+
+            pw.printf("%sReports all issues on user-type package allowlist XML files. Options:\n",
+                    PREFIX_HELP_DESCRIPTION);
+            pw.printf("%s%s | %s: shows extra info, like number of issues\n",
+                    PREFIX_HELP_DESCRIPTION, ARG_V, ARG_VERBOSE);
+            pw.printf("%s%s: show only critical issues, excluding warnings\n",
+                    PREFIX_HELP_DESCRIPTION, ARG_CRITICAL_ONLY);
+            pw.printf("%s%s MODE: shows what errors would be if device used mode MODE\n"
+                    + "%s(where MODE is the allowlist mode integer as defined by "
+                    + "config_userTypePackageWhitelistMode)\n\n",
+                    PREFIX_HELP_DESCRIPTION, ARG_MODE, PREFIX_HELP_DESCRIPTION_EXTRA_LINES);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index 46fde4b..6060233 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -272,6 +272,10 @@
                 mHandler.removeCallbacksAndMessages(mToken);
 
                 if (importance > IMPORTANCE_CACHED) {
+                    if (mRevokeAfterKilledDelay == 0) {
+                        onPackageInactiveLocked();
+                        return;
+                    }
                     // Delay revocation in case app is restarting
                     mHandler.postDelayed(() -> {
                         int imp = mActivityManager.getUidImportance(mUid);
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 71554ee..5a05134b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -562,12 +562,6 @@
     }
 
     @Override
-    public void revokeOwnPermissionsOnKill(@NonNull String packageName,
-            @NonNull List<String> permissions) {
-        mPermissionManagerServiceImpl.revokeOwnPermissionsOnKill(packageName, permissions);
-    }
-
-    @Override
     public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
             int userId) {
         return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index d060930..009d155 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -1597,25 +1597,6 @@
         }
     }
 
-    @Override
-    public void revokeOwnPermissionsOnKill(String packageName, List<String> permissions) {
-        final int callingUid = Binder.getCallingUid();
-        int callingUserId = UserHandle.getUserId(callingUid);
-        int targetPackageUid = mPackageManagerInt.getPackageUid(packageName, 0, callingUserId);
-        if (targetPackageUid != callingUid) {
-            throw new SecurityException("uid " + callingUid
-                    + " cannot revoke permissions for package " + packageName + " with uid "
-                    + targetPackageUid);
-        }
-        for (String permName : permissions) {
-            if (!checkCallingOrSelfPermission(permName)) {
-                throw new SecurityException("uid " + callingUid + " cannot revoke permission "
-                        + permName + " because it does not hold that permission");
-            }
-        }
-        mPermissionControllerManager.revokeOwnPermissionsOnKill(packageName, permissions);
-    }
-
     private boolean mayManageRolePermission(int uid) {
         final PackageManager packageManager = mContext.getPackageManager();
         final String[] packageNames = packageManager.getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 3e28320..3771f03 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -327,28 +327,6 @@
     void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId);
 
     /**
-     * Triggers the revocation of one or more permissions for a package, under the following
-     * conditions:
-     * <ul>
-     * <li>The package {@code packageName} must be under the same UID as the calling process
-     *   (typically, the target package is the calling package).
-     * <li>Each permission in {@code permissions} must be granted to the package
-     * {@code packageName}.
-     * <li>Each permission in {@code permissions} must be a runtime permission.
-     * </ul>
-     * <p>
-     * Background permissions which have no corresponding foreground permission still granted once
-     * the revocation is effective will also be revoked.
-     * <p>
-     * This revocation happens asynchronously and kills all processes running in the same UID as
-     * {@code packageName}. It will be triggered once it is safe to do so.
-     *
-     * @param packageName The name of the package for which the permissions will be revoked.
-     * @param permissions List of permissions to be revoked.
-     */
-    void revokeOwnPermissionsOnKill(String packageName, List<String> permissions);
-
-    /**
      * Get whether you should show UI with rationale for requesting a permission. You should do this
      * only if you do not have the permission and the context in which the permission is requested
      * does not clearly communicate to the user what would be the benefit from grating this
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 2e60f13..526dccb 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1633,7 +1633,14 @@
         if (adapter != null) {
             SynchronousResultReceiver bluetoothReceiver =
                     new SynchronousResultReceiver("bluetooth");
-            adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
+            adapter.requestControllerActivityEnergyInfo(
+                    Runnable::run,
+                    info -> {
+                        Bundle bundle = new Bundle();
+                        bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
+                        bluetoothReceiver.send(0, bundle);
+                    }
+            );
             return awaitControllerInfo(bluetoothReceiver);
         } else {
             Slog.e(TAG, "Failed to get bluetooth adapter!");
@@ -2338,51 +2345,25 @@
     }
 
     int pullProcessDmabufMemory(int atomTag, List<StatsEvent> pulledData) {
-        List<ProcessMemoryState> managedProcessList =
-                LocalServices.getService(ActivityManagerInternal.class)
-                        .getMemoryStateForProcesses();
-        for (ProcessMemoryState process : managedProcessList) {
-            KernelAllocationStats.ProcessDmabuf proc =
-                    KernelAllocationStats.getDmabufAllocations(process.pid);
-            if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) {
-                continue;
-            }
-            pulledData.add(
-                    FrameworkStatsLog.buildStatsEvent(
-                            atomTag,
-                            process.uid,
-                            process.processName,
-                            process.oomScore,
-                            proc.retainedSizeKb,
-                            proc.retainedBuffersCount,
-                            proc.mappedSizeKb,
-                            proc.mappedBuffersCount));
+        KernelAllocationStats.ProcessDmabuf[] procBufs =
+                KernelAllocationStats.getDmabufAllocations();
+
+        if (procBufs == null) {
+            return StatsManager.PULL_SKIP;
         }
-        SparseArray<String> processCmdlines = getProcessCmdlines();
-        managedProcessList.forEach(managedProcess -> processCmdlines.delete(managedProcess.pid));
-        int size = processCmdlines.size();
-        for (int i = 0; i < size; ++i) {
-            int pid = processCmdlines.keyAt(i);
-            int uid = getUidForPid(pid);
-            // ignore root processes (unlikely to be interesting)
-            if (uid <= 0) {
-                continue;
-            }
-            KernelAllocationStats.ProcessDmabuf proc =
-                    KernelAllocationStats.getDmabufAllocations(pid);
-            if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) {
-                continue;
-            }
-            pulledData.add(
-                    FrameworkStatsLog.buildStatsEvent(
-                            atomTag,
-                            uid,
-                            processCmdlines.valueAt(i),
-                            -1001 /*Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.*/,
-                            proc.retainedSizeKb,
-                            proc.retainedBuffersCount,
-                            proc.mappedSizeKb,
-                            proc.mappedBuffersCount));
+        for (KernelAllocationStats.ProcessDmabuf procBuf : procBufs) {
+            pulledData.add(FrameworkStatsLog.buildStatsEvent(
+                    atomTag,
+                    procBuf.uid,
+                    procBuf.processName,
+                    procBuf.oomScore,
+                    procBuf.retainedSizeKb,
+                    procBuf.retainedBuffersCount,
+                    0, /* mapped_dmabuf_kb - deprecated */
+                    0, /* mapped_dmabuf_count - deprecated */
+                    procBuf.surfaceFlingerSizeKb,
+                    procBuf.surfaceFlingerCount
+            ));
         }
         return StatsManager.PULL_SUCCESS;
     }
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index 53a9244..6adf733d 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -42,8 +42,8 @@
 import android.media.tv.interactive.ITvInteractiveAppServiceCallback;
 import android.media.tv.interactive.ITvInteractiveAppSession;
 import android.media.tv.interactive.ITvInteractiveAppSessionCallback;
-import android.media.tv.interactive.TvInteractiveAppInfo;
 import android.media.tv.interactive.TvInteractiveAppService;
+import android.media.tv.interactive.TvInteractiveAppServiceInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -130,7 +130,7 @@
                 new Intent(TvInteractiveAppService.SERVICE_INTERFACE),
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
                 userId);
-        List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
+        List<TvInteractiveAppServiceInfo> iAppList = new ArrayList<>();
 
         for (ResolveInfo ri : services) {
             ServiceInfo si = ri.serviceInfo;
@@ -143,8 +143,8 @@
 
             ComponentName component = new ComponentName(si.packageName, si.name);
             try {
-                TvInteractiveAppInfo info =
-                        new TvInteractiveAppInfo(mContext, component);
+                TvInteractiveAppServiceInfo info =
+                        new TvInteractiveAppServiceInfo(mContext, component);
                 iAppList.add(info);
             } catch (Exception e) {
                 Slogf.e(TAG, "failed to load TV Interactive App service " + si.name, e);
@@ -154,10 +154,10 @@
         }
 
         // sort the iApp list by iApp service id
-        Collections.sort(iAppList, Comparator.comparing(TvInteractiveAppInfo::getId));
+        Collections.sort(iAppList, Comparator.comparing(TvInteractiveAppServiceInfo::getId));
         Map<String, TvInteractiveAppState> iAppMap = new HashMap<>();
         ArrayMap<String, Integer> tiasAppCount = new ArrayMap<>(iAppMap.size());
-        for (TvInteractiveAppInfo info : iAppList) {
+        for (TvInteractiveAppServiceInfo info : iAppList) {
             String iAppServiceId = info.getId();
             if (DEBUG) {
                 Slogf.d(TAG, "add " + iAppServiceId);
@@ -195,7 +195,7 @@
 
         for (String iAppServiceId : userState.mIAppMap.keySet()) {
             if (!iAppMap.containsKey(iAppServiceId)) {
-                TvInteractiveAppInfo info = userState.mIAppMap.get(iAppServiceId).mInfo;
+                TvInteractiveAppServiceInfo info = userState.mIAppMap.get(iAppServiceId).mInfo;
                 ServiceState serviceState = userState.mServiceStateMap.get(info.getComponent());
                 if (serviceState != null) {
                     abortPendingCreateSessionRequestsLocked(serviceState, iAppServiceId, userId);
@@ -283,7 +283,7 @@
         userState.mCallbacks.finishBroadcast();
     }
 
-    private int getInteractiveAppUid(TvInteractiveAppInfo info) {
+    private int getInteractiveAppUid(TvInteractiveAppServiceInfo info) {
         try {
             return getContext().getPackageManager().getApplicationInfo(
                     info.getServiceInfo().packageName, 0).uid;
@@ -642,7 +642,7 @@
     private final class BinderService extends ITvInteractiveAppManager.Stub {
 
         @Override
-        public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId) {
+        public List<TvInteractiveAppServiceInfo> getTvInteractiveAppServiceList(int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, "getTvInteractiveAppServiceList");
             final long identity = Binder.clearCallingIdentity();
@@ -653,7 +653,7 @@
                         mGetServiceListCalled = true;
                     }
                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
+                    List<TvInteractiveAppServiceInfo> iAppList = new ArrayList<>();
                     for (TvInteractiveAppState state : userState.mIAppMap.values()) {
                         iAppList.add(state.mInfo);
                     }
@@ -1738,7 +1738,7 @@
     private static final class TvInteractiveAppState {
         private String mIAppServiceId;
         private ComponentName mComponentName;
-        private TvInteractiveAppInfo mInfo;
+        private TvInteractiveAppServiceInfo mInfo;
         private int mUid;
         private int mIAppNumber;
     }
diff --git a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
index 397acfa..b45c962 100644
--- a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
+++ b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
@@ -88,7 +88,7 @@
 
     @Override
     public void traceBegin(@NonNull String name) {
-        Slog.i(mTag, name);
+        Slog.d(mTag, name);
         super.traceBegin(name);
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 543e44c..ffc4388 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -746,8 +746,9 @@
                     // if it is not already expanding to fullscreen. Otherwise, the arguments will
                     // be used the next time the activity enters PiP.
                     final Task rootTask = r.getRootTask();
-                    rootTask.setPictureInPictureAspectRatio(r.pictureInPictureArgs.getAspectRatio(),
-                            r.pictureInPictureArgs.getExpandedAspectRatio());
+                    rootTask.setPictureInPictureAspectRatio(
+                            r.pictureInPictureArgs.getAspectRatioFloat(),
+                            r.pictureInPictureArgs.getExpandedAspectRatioFloat());
                     rootTask.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
                 }
             }
@@ -828,7 +829,7 @@
 
         if (params.hasSetAspectRatio()
                 && !mService.mWindowManager.isValidPictureInPictureAspectRatio(
-                r.mDisplayContent, params.getAspectRatio())) {
+                r.mDisplayContent, params.getAspectRatioFloat())) {
             throw new IllegalArgumentException(String.format(caller
                             + ": Aspect ratio is too extreme (must be between %f and %f).",
                     minAspectRatio, maxAspectRatio));
@@ -836,7 +837,7 @@
 
         if (mService.mSupportsExpandedPictureInPicture && params.hasSetExpandedAspectRatio()
                 && !mService.mWindowManager.isValidExpandedPictureInPictureAspectRatio(
-                r.mDisplayContent, params.getExpandedAspectRatio())) {
+                r.mDisplayContent, params.getExpandedAspectRatioFloat())) {
             throw new IllegalArgumentException(String.format(caller
                             + ": Expanded aspect ratio is not extreme enough (must not be between"
                             + " %f and %f).",
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index c0cdec9..7f84f61 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -191,6 +191,35 @@
         private long mCurrentTransitionStartTimeNs;
         /** Non-null when a {@link TransitionInfo} is created for this state. */
         private TransitionInfo mAssociatedTransitionInfo;
+        /** The sequence id for trace. It is used to map the traces before resolving intent. */
+        private static int sTraceSeqId;
+        /** The trace format is "launchingActivity#$seqId:$state(:$packageName)". */
+        final String mTraceName;
+
+        LaunchingState() {
+            if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+                mTraceName = null;
+                return;
+            }
+            // Use an id because the launching app is not yet known before resolving intent.
+            sTraceSeqId++;
+            mTraceName = "launchingActivity#" + sTraceSeqId;
+            Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName, 0);
+        }
+
+        void stopTrace(boolean abort) {
+            if (mTraceName == null) return;
+            Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName, 0);
+            final String launchResult;
+            if (mAssociatedTransitionInfo == null) {
+                launchResult = ":failed";
+            } else {
+                launchResult = (abort ? ":canceled:" : ":completed:")
+                        + mAssociatedTransitionInfo.mLastLaunchedActivity.packageName;
+            }
+            // Put a supplement trace as the description of the async trace with the same id.
+            Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName + launchResult);
+        }
 
         @VisibleForTesting
         boolean allDrawn() {
@@ -549,10 +578,10 @@
         }
 
         if (existingInfo == null) {
-            // Only notify the observer for a new launching event.
-            launchObserverNotifyIntentStarted(intent, transitionStartTimeNs);
             final LaunchingState launchingState = new LaunchingState();
             launchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs;
+            // Only notify the observer for a new launching event.
+            launchObserverNotifyIntentStarted(intent, transitionStartTimeNs);
             return launchingState;
         }
         existingInfo.mLaunchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs;
@@ -574,7 +603,7 @@
             @Nullable ActivityOptions options) {
         if (launchedActivity == null) {
             // The launch is aborted, e.g. intent not resolved, class not found.
-            abort(null /* info */, "nothing launched");
+            abort(launchingState, "nothing launched");
             return;
         }
 
@@ -601,7 +630,7 @@
 
         if (launchedActivity.isReportedDrawn() && launchedActivity.isVisible()) {
             // Launched activity is already visible. We cannot measure windows drawn delay.
-            abort(info, "launched activity already visible");
+            abort(launchingState, "launched activity already visible");
             return;
         }
 
@@ -633,7 +662,7 @@
         final TransitionInfo newInfo = TransitionInfo.create(launchedActivity, launchingState,
                 options, processRunning, processSwitch, newActivityCreated, resultCode);
         if (newInfo == null) {
-            abort(info, "unrecognized launch");
+            abort(launchingState, "unrecognized launch");
             return;
         }
 
@@ -874,23 +903,29 @@
         }
     }
 
+    private void abort(@NonNull LaunchingState state, String cause) {
+        if (state.mAssociatedTransitionInfo != null) {
+            abort(state.mAssociatedTransitionInfo, cause);
+            return;
+        }
+        if (DEBUG_METRICS) Slog.i(TAG, "abort launch cause=" + cause);
+        state.stopTrace(true /* abort */);
+        launchObserverNotifyIntentFailed();
+    }
+
     /** Aborts tracking of current launch metrics. */
-    private void abort(TransitionInfo info, String cause) {
+    private void abort(@NonNull TransitionInfo info, String cause) {
         done(true /* abort */, info, cause, 0L /* timestampNs */);
     }
 
     /** Called when the given transition (info) is no longer active. */
-    private void done(boolean abort, @Nullable TransitionInfo info, String cause,
+    private void done(boolean abort, @NonNull TransitionInfo info, String cause,
             long timestampNs) {
         if (DEBUG_METRICS) {
             Slog.i(TAG, "done abort=" + abort + " cause=" + cause + " timestamp=" + timestampNs
                     + " info=" + info);
         }
-        if (info == null) {
-            launchObserverNotifyIntentFailed();
-            return;
-        }
-
+        info.mLaunchingState.stopTrace(abort);
         stopLaunchTrace(info);
         final Boolean isHibernating =
                 mLastHibernationStates.remove(info.mLastLaunchedActivity.packageName);
@@ -1457,7 +1492,7 @@
     /** Starts trace for an activity is actually launching. */
     private void startLaunchTrace(@NonNull TransitionInfo info) {
         if (DEBUG_METRICS) Slog.i(TAG, "startLaunchTrace " + info);
-        if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+        if (info.mLaunchingState.mTraceName == null) {
             return;
         }
         info.mLaunchTraceName = "launching: " + info.mLastLaunchedActivity.packageName;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 883ce99..bfe1f30 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5603,19 +5603,8 @@
                     "makeInvisible", true /* beforeStopping */);
             // Defer telling the client it is hidden if it can enter Pip and isn't current paused,
             // stopped or stopping. This gives it a chance to enter Pip in onPause().
-            // TODO: There is still a question surrounding activities in multi-window mode that want
-            // to enter Pip after they are paused, but are still visible. I they should be okay to
-            // enter Pip in those cases, but not "auto-Pip" which is what this condition covers and
-            // the current contract for "auto-Pip" is that the app should enter it before onPause
-            // returns. Just need to confirm this reasoning makes sense.
             final boolean deferHidingClient = canEnterPictureInPicture
                     && !isState(STARTED, STOPPING, STOPPED, PAUSED);
-            if (!mTransitionController.isShellTransitionsEnabled()
-                    && deferHidingClient && pictureInPictureArgs.isAutoEnterEnabled()) {
-                // Go ahead and just put the activity in pip if it supports auto-pip.
-                mAtmService.enterPictureInPictureMode(this, pictureInPictureArgs);
-                return;
-            }
             setDeferHidingClient(deferHidingClient);
             setVisibility(false);
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 7d2dfa0..77ec67f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2730,10 +2730,11 @@
                                 false /* includingParents */);
                         intentTask = intentTask.getParent().asTaskFragment().getTask();
                     }
-                    // If the task is in multi-windowing mode, the activity may already be on
+                    // If the activity is visible in multi-windowing mode, it may already be on
                     // the top (visible to user but not the global top), then the result code
                     // should be START_DELIVERED_TO_TOP instead of START_TASK_TO_FRONT.
                     final boolean wasTopOfVisibleRootTask = intentActivity.mVisibleRequested
+                            && intentActivity.inMultiWindowMode()
                             && intentActivity == mTargetRootTask.topRunningActivity();
                     // We only want to move to the front, if we aren't going to launch on a
                     // different root task. If we launch on a different root task, we will put the
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index b5312c4..988a7c6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3523,8 +3523,9 @@
                 }
                 // Only update the saved args from the args that are set
                 r.setPictureInPictureParams(params);
-                final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
-                final float expandedAspectRatio = r.pictureInPictureArgs.getExpandedAspectRatio();
+                final float aspectRatio = r.pictureInPictureArgs.getAspectRatioFloat();
+                final float expandedAspectRatio =
+                        r.pictureInPictureArgs.getExpandedAspectRatioFloat();
                 final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
                 mRootWindowContainer.moveActivityToPinnedRootTask(r,
                         null /* launchIntoPipHostActivity */, "enterPictureInPictureMode");
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 07a0c37..87523f4 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONTENT_RECORDING;
 
@@ -26,6 +27,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.provider.DeviceConfig;
 import android.view.ContentRecordingSession;
 import android.view.Display;
 import android.view.SurfaceControl;
@@ -39,6 +41,11 @@
 final class ContentRecorder {
 
     /**
+     * The key for accessing the device config that controls if task recording is supported.
+     */
+    @VisibleForTesting static final String KEY_RECORD_TASK_FEATURE = "record_task_content";
+
+    /**
      * The display content this class is handling recording for.
      */
     @NonNull
@@ -48,7 +55,7 @@
      * The session for content recording, or null if this DisplayContent is not being used for
      * recording.
      */
-    @VisibleForTesting private ContentRecordingSession mContentRecordingSession = null;
+    private ContentRecordingSession mContentRecordingSession = null;
 
     /**
      * The WindowContainer for the level of the hierarchy to record.
@@ -187,6 +194,8 @@
             mDisplayContent.mWmService.mTransactionFactory.get().remove(mRecordedSurface).apply();
             mRecordedSurface = null;
             clearContentRecordingSession();
+            // Do not need to force remove the VirtualDisplay; this is handled by the media
+            // projection service.
         }
     }
 
@@ -215,46 +224,12 @@
             return;
         }
 
-        final int contentToRecord = mContentRecordingSession.getContentToRecord();
-        if (contentToRecord != RECORD_CONTENT_DISPLAY) {
-            // TODO(b/216625226) handle task-based recording
-            // Not a valid region, or recording is disabled, so fall back to prior MediaProjection
-            // approach.
-            clearContentRecordingSession();
-            ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                    "Unable to start recording due to invalid region for display %d",
-                    mDisplayContent.getDisplayId());
+        mRecordedWindowContainer = retrieveRecordedWindowContainer();
+        if (mRecordedWindowContainer == null) {
+            // Either the token is missing, or the window associated with the token is missing.
+            // Error has already been handled, so just leave.
             return;
         }
-        // Given the WindowToken of the DisplayArea to record, retrieve the associated
-        // SurfaceControl.
-        IBinder tokenToRecord = mContentRecordingSession.getTokenToRecord();
-        if (tokenToRecord == null) {
-            // Unexpectedly missing token. Fall back to prior MediaProjection approach.
-            clearContentRecordingSession();
-            ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                    "Unable to start recording due to null token for display %d",
-                    mDisplayContent.getDisplayId());
-            return;
-        }
-
-        final WindowContainer wc =
-                mDisplayContent.mWmService.mWindowContextListenerController.getContainer(
-                        tokenToRecord);
-        if (wc == null) {
-            // Un-set the window token to record for this VirtualDisplay. Fall back to the
-            // original MediaProjection approach.
-            mDisplayContent.mWmService.mDisplayManagerInternal.setWindowManagerMirroring(
-                    mDisplayContent.getDisplayId(), false);
-            clearContentRecordingSession();
-            ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
-                    "Unable to retrieve window container to start recording for "
-                            + "display %d",
-                    mDisplayContent.getDisplayId());
-            return;
-        }
-        // TODO(206461622) Migrate to using the RootDisplayArea
-        mRecordedWindowContainer = wc.getDisplayContent();
 
         final Point surfaceSize = fetchSurfaceSizeIfPresent();
         if (surfaceSize == null) {
@@ -296,6 +271,107 @@
     }
 
     /**
+     * Retrieves the {@link WindowContainer} for the level of the hierarchy to start recording,
+     * indicated by the {@link #mContentRecordingSession}. Performs any error handling and state
+     * updates necessary if the {@link WindowContainer} could not be retrieved.
+     * {@link #mContentRecordingSession} must be non-null.
+     *
+     * @return a {@link WindowContainer} to record, or {@code null} if an error was encountered. The
+     * error is logged and any cleanup is handled.
+     */
+    @Nullable
+    private WindowContainer retrieveRecordedWindowContainer() {
+        final int contentToRecord = mContentRecordingSession.getContentToRecord();
+        // Given the WindowToken of the region to record, retrieve the associated
+        // SurfaceControl.
+        final IBinder tokenToRecord = mContentRecordingSession.getTokenToRecord();
+        if (tokenToRecord == null) {
+            handleStartRecordingFailed();
+            ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+                    "Unable to start recording due to null token for display %d",
+                    mDisplayContent.getDisplayId());
+            return null;
+        }
+        switch (contentToRecord) {
+            case RECORD_CONTENT_DISPLAY:
+                final WindowContainer wc =
+                        mDisplayContent.mWmService.mWindowContextListenerController.getContainer(
+                                tokenToRecord);
+                if (wc == null) {
+                    // Un-set the window token to record for this VirtualDisplay. Fall back to
+                    // Display stack capture for the entire display.
+                    mDisplayContent.mWmService.mDisplayManagerInternal.setWindowManagerMirroring(
+                            mDisplayContent.getDisplayId(), false);
+                    handleStartRecordingFailed();
+                    ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+                            "Unable to retrieve window container to start recording for "
+                                    + "display %d", mDisplayContent.getDisplayId());
+                    return null;
+                }
+                // TODO(206461622) Migrate to using the RootDisplayArea
+                return wc.getDisplayContent();
+            case RECORD_CONTENT_TASK:
+                if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                        KEY_RECORD_TASK_FEATURE, false)) {
+                    handleStartRecordingFailed();
+                    ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+                            "Unable to record task since feature is disabled %d",
+                            mDisplayContent.getDisplayId());
+                    return null;
+                }
+                Task taskToRecord = WindowContainer.fromBinder(tokenToRecord).asTask();
+                if (taskToRecord == null) {
+                    handleStartRecordingFailed();
+                    ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+                            "Unable to retrieve task to start recording for "
+                                    + "display %d", mDisplayContent.getDisplayId());
+                }
+                return taskToRecord;
+            default:
+                // Not a valid region, or recording is disabled, so fall back to Display stack
+                // capture for the entire display.
+                handleStartRecordingFailed();
+                ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+                        "Unable to start recording due to invalid region for display %d",
+                        mDisplayContent.getDisplayId());
+                return null;
+        }
+    }
+
+    /**
+     * Exit this recording session.
+     * <p>
+     * If this is a task session, tear down the recording entirely. Do not fall back
+     * to recording the entire display on the display stack; this would surprise the user
+     * given they selected task capture.
+     * </p><p>
+     * If this is a display session, just stop recording by layer mirroring. Fall back to recording
+     * from the display stack.
+     * </p>
+     */
+    private void handleStartRecordingFailed() {
+        final boolean shouldExitTaskRecording = mContentRecordingSession != null
+                && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK;
+        if (shouldExitTaskRecording) {
+            // Clean up the cached session first, since tearing down the display will generate
+            // display
+            // events which will trickle back to here.
+            clearContentRecordingSession();
+            tearDownVirtualDisplay();
+        } else {
+            clearContentRecordingSession();
+        }
+    }
+
+    /**
+     * Ensure recording does not fall back to the display stack; ensure the recording is stopped
+     * and the client notified by tearing down the virtual display.
+     */
+    private void tearDownVirtualDisplay() {
+        // TODO(b/219761722) Clean up the VirtualDisplay if task mirroring fails
+    }
+
+    /**
      * Apply transformations to the mirrored surface to ensure the captured contents are scaled to
      * fit and centred in the output surface.
      *
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 597d29f..2ac41a7 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -63,8 +63,6 @@
 import static com.android.server.wm.TaskFragmentProto.MIN_HEIGHT;
 import static com.android.server.wm.TaskFragmentProto.MIN_WIDTH;
 import static com.android.server.wm.TaskFragmentProto.WINDOW_CONTAINER;
-import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.TASK_FRAGMENT;
 
 import android.annotation.IntDef;
@@ -1461,7 +1459,8 @@
             // next activity.
             final boolean lastResumedCanPip = prev.checkEnterPictureInPictureState(
                     "shouldAutoPipWhilePausing", userLeaving);
-            if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
+            if (userLeaving && lastResumedCanPip
+                    && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
                 shouldAutoPip = true;
             } else if (!lastResumedCanPip) {
                 // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
@@ -2370,8 +2369,7 @@
         if (!hasChild()) {
             return false;
         }
-        return isAnimating(TRANSITION | CHILDREN, WindowState.EXIT_ANIMATING_TYPES)
-                || inTransition();
+        return isExitAnimationRunningSelfOrChild() || inTransition();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index dab02de..e7b4e83 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1196,6 +1196,23 @@
         return false;
     }
 
+    boolean isExitAnimationRunningSelfOrChild() {
+        if (!mTransitionController.isShellTransitionsEnabled()) {
+            return isAnimating(TRANSITION | CHILDREN, WindowState.EXIT_ANIMATING_TYPES);
+        }
+        if (mTransitionController.isCollecting(this)) {
+            return true;
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            WindowContainer child = mChildren.get(i);
+            if (child.isExitAnimationRunningSelfOrChild()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void sendAppVisibilityToClients() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d5f7a22..eb88b8b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -119,7 +119,6 @@
 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
-import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
@@ -2503,8 +2502,6 @@
             ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
                     win, focusMayChange);
 
-            result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
-
             if (DEBUG_LAYOUT) {
                 Slog.v(TAG_WM, "Relayout complete " + win + ": outFrames=" + outFrames);
             }
@@ -2563,8 +2560,7 @@
         if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
             focusMayChange = true;
             win.mAnimatingExit = true;
-        } else if (win.mDisplayContent.okToAnimate() && win.isAnimating(TRANSITION | PARENTS,
-                WindowState.EXIT_ANIMATING_TYPES)) {
+        } else if (win.mDisplayContent.okToAnimate() && win.isExitAnimationRunningSelfOrParent()) {
             // Currently in a hide animation... turn this into
             // an exit.
             win.mAnimatingExit = true;
@@ -3282,7 +3278,7 @@
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE,
                 Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE
-                        + " permission required to read keyguard visibility");
+                        + " permission required to subscribe to keyguard locked state changes");
     }
 
     private void dispatchKeyguardLockedState() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f667392..60e196c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -134,6 +134,7 @@
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.WINDOW;
@@ -2610,8 +2611,7 @@
                         mWmService.mAccessibilityController.onWindowTransition(this, transit);
                     }
                 }
-                final boolean isAnimating = mAnimatingExit
-                        || isAnimating(TRANSITION | PARENTS, EXIT_ANIMATING_TYPES);
+                final boolean isAnimating = mAnimatingExit || isExitAnimationRunningSelfOrParent();
                 final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null
                         && mActivityRecord.isLastWindow(this);
                 // We delay the removal of a window if it has a showing surface that can be used to run
@@ -4977,6 +4977,15 @@
         return false;
     }
 
+    boolean isExitAnimationRunningSelfOrParent() {
+        return inAppOrRecentsTransition()
+                || isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
+    }
+
+    boolean isExitAnimationRunningSelfOrChild() {
+        return isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION);
+    }
+
     private boolean shouldFinishAnimatingExit() {
         // Exit animation might be applied soon.
         if (inTransition()) {
@@ -4988,7 +4997,7 @@
             return true;
         }
         // Exit animation is running.
-        if (isAnimating(TRANSITION | PARENTS, EXIT_ANIMATING_TYPES)) {
+        if (isExitAnimationRunningSelfOrParent()) {
             ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "shouldWaitAnimatingExit: isAnimating: %s",
                     this);
             return false;
@@ -5081,7 +5090,7 @@
 
     @Override
     boolean handleCompleteDeferredRemoval() {
-        if (mRemoveOnExit && !isSelfAnimating(0 /* flags */, EXIT_ANIMATING_TYPES)) {
+        if (mRemoveOnExit && !isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)) {
             mRemoveOnExit = false;
             removeImmediately();
         }
diff --git a/services/core/jni/gnss/MeasurementCorrections.cpp b/services/core/jni/gnss/MeasurementCorrections.cpp
index 8a3d84c..07d0a45 100644
--- a/services/core/jni/gnss/MeasurementCorrections.cpp
+++ b/services/core/jni/gnss/MeasurementCorrections.cpp
@@ -44,6 +44,7 @@
 using ReflectingPlane_V1_0 =
         android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane;
 using ReflectingPlane_Aidl = android::hardware::gnss::measurement_corrections::ReflectingPlane;
+using ExcessPathInfo = SingleSatCorrection_Aidl::ExcessPathInfo;
 using GnssConstellationType_V1_0 = android::hardware::gnss::V1_0::GnssConstellationType;
 using GnssConstellationType_V2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
 using GnssConstellationType_Aidl = android::hardware::gnss::GnssConstellationType;
@@ -62,7 +63,7 @@
 jmethodID method_correctionsGetEnvironmentBearingDegrees;
 jmethodID method_correctionsGetEnvironmentBearingUncertaintyDegrees;
 jmethodID method_listSize;
-jmethodID method_correctionListGet;
+jmethodID method_listGet;
 jmethodID method_correctionSatFlags;
 jmethodID method_correctionSatConstType;
 jmethodID method_correctionSatId;
@@ -71,10 +72,17 @@
 jmethodID method_correctionSatEpl;
 jmethodID method_correctionSatEplUnc;
 jmethodID method_correctionSatRefPlane;
+jmethodID method_correctionSatAttenuation;
+jmethodID method_correctionSatExcessPathInfoList;
 jmethodID method_correctionPlaneLatDeg;
 jmethodID method_correctionPlaneLngDeg;
 jmethodID method_correctionPlaneAltDeg;
 jmethodID method_correctionPlaneAzimDeg;
+jmethodID method_excessPathInfoFlags;
+jmethodID method_excessPathInfoEpl;
+jmethodID method_excessPathInfoEplUnc;
+jmethodID method_excessPathInfoRefPlane;
+jmethodID method_excessPathInfoAttenuation;
 } // anonymous namespace
 
 void MeasurementCorrections_class_init_once(JNIEnv* env, jclass clazz) {
@@ -103,7 +111,7 @@
 
     jclass corrListClass = env->FindClass("java/util/List");
     method_listSize = env->GetMethodID(corrListClass, "size", "()I");
-    method_correctionListGet = env->GetMethodID(corrListClass, "get", "(I)Ljava/lang/Object;");
+    method_listGet = env->GetMethodID(corrListClass, "get", "(I)Ljava/lang/Object;");
 
     jclass singleSatCorrClass = env->FindClass("android/location/GnssSingleSatCorrection");
     method_correctionSatFlags =
@@ -121,12 +129,27 @@
             env->GetMethodID(singleSatCorrClass, "getExcessPathLengthUncertaintyMeters", "()F");
     method_correctionSatRefPlane = env->GetMethodID(singleSatCorrClass, "getReflectingPlane",
                                                     "()Landroid/location/GnssReflectingPlane;");
+    method_correctionSatAttenuation =
+            env->GetMethodID(singleSatCorrClass, "getCombinedAttenuationDb", "()F");
+    method_correctionSatExcessPathInfoList =
+            env->GetMethodID(singleSatCorrClass, "getGnssExcessPathInfoList", "()Ljava/util/List;");
 
     jclass refPlaneClass = env->FindClass("android/location/GnssReflectingPlane");
     method_correctionPlaneLatDeg = env->GetMethodID(refPlaneClass, "getLatitudeDegrees", "()D");
     method_correctionPlaneLngDeg = env->GetMethodID(refPlaneClass, "getLongitudeDegrees", "()D");
     method_correctionPlaneAltDeg = env->GetMethodID(refPlaneClass, "getAltitudeMeters", "()D");
     method_correctionPlaneAzimDeg = env->GetMethodID(refPlaneClass, "getAzimuthDegrees", "()D");
+
+    jclass excessPathInfoClass = env->FindClass("android/location/GnssExcessPathInfo");
+    method_excessPathInfoFlags = env->GetMethodID(excessPathInfoClass, "getFlags", "()I");
+    method_excessPathInfoEpl =
+            env->GetMethodID(excessPathInfoClass, "getExcessPathLengthMeters", "()F");
+    method_excessPathInfoEplUnc =
+            env->GetMethodID(excessPathInfoClass, "getExcessPathLengthUncertaintyMeters", "()F");
+    method_excessPathInfoRefPlane = env->GetMethodID(excessPathInfoClass, "getReflectingPlane",
+                                                     "()Landroid/location/GnssReflectingPlane;");
+    method_excessPathInfoAttenuation =
+            env->GetMethodID(excessPathInfoClass, "getAttenuationDb", "()F");
 }
 
 template <>
@@ -324,7 +347,8 @@
 SingleSatCorrection_V1_0
 MeasurementCorrectionsUtil::getSingleSatCorrection_1_0_withoutConstellation(
         JNIEnv* env, jobject singleSatCorrectionObj) {
-    jint correctionFlags = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags);
+    uint16_t corrFlags = static_cast<uint16_t>(
+            env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags));
     jint satId = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId);
     jfloat carrierFreqHz =
             env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatCarrierFreq);
@@ -332,14 +356,16 @@
             env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatIsLosProb);
     jfloat eplMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl);
     jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEplUnc);
-    uint16_t corrFlags = static_cast<uint16_t>(correctionFlags);
 
     ReflectingPlane_V1_0 reflectingPlane;
-    if ((corrFlags & GnssSingleSatCorrectionFlags_V1_0::HAS_REFLECTING_PLANE) != 0)
-        MeasurementCorrectionsUtil::getReflectingPlane<ReflectingPlane_V1_0>(env,
-                                                                             singleSatCorrectionObj,
+    if ((corrFlags & GnssSingleSatCorrectionFlags_V1_0::HAS_REFLECTING_PLANE) != 0) {
+        jobject reflectingPlaneObj =
+                env->CallObjectMethod(singleSatCorrectionObj, method_correctionSatRefPlane);
+        MeasurementCorrectionsUtil::setReflectingPlane<ReflectingPlane_V1_0>(env,
+                                                                             reflectingPlaneObj,
                                                                              reflectingPlane);
-
+        env->DeleteLocalRef(reflectingPlaneObj);
+    }
     SingleSatCorrection_V1_0 singleSatCorrection = {
             .singleSatCorrectionFlags = corrFlags,
             .svid = static_cast<uint16_t>(satId),
@@ -349,13 +375,14 @@
             .excessPathLengthUncertaintyMeters = eplUncMeters,
             .reflectingPlane = reflectingPlane,
     };
-
     return singleSatCorrection;
 }
 
 SingleSatCorrection_Aidl MeasurementCorrectionsUtil::getSingleSatCorrection_Aidl(
         JNIEnv* env, jobject singleSatCorrectionObj) {
-    jint correctionFlags = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags);
+    int32_t corrFlags = static_cast<int32_t>(
+            env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags));
+    jint constType = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatConstType);
     jint satId = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId);
     jfloat carrierFreqHz =
             env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatCarrierFreq);
@@ -363,15 +390,10 @@
             env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatIsLosProb);
     jfloat eplMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl);
     jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEplUnc);
-    int32_t corrFlags = static_cast<int32_t>(correctionFlags);
-
-    ReflectingPlane_Aidl reflectingPlane;
-    if ((corrFlags & SingleSatCorrection_Aidl::SINGLE_SAT_CORRECTION_HAS_REFLECTING_PLANE) != 0)
-        MeasurementCorrectionsUtil::getReflectingPlane<ReflectingPlane_Aidl>(env,
-                                                                             singleSatCorrectionObj,
-                                                                             reflectingPlane);
-
-    jint constType = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatConstType);
+    jfloat attenuationDb =
+            env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatAttenuation);
+    std::vector<ExcessPathInfo> excessPathInfos =
+            MeasurementCorrectionsUtil::getExcessPathInfoList(env, singleSatCorrectionObj);
 
     SingleSatCorrection_Aidl singleSatCorrection;
     singleSatCorrection.singleSatCorrectionFlags = corrFlags;
@@ -379,9 +401,10 @@
     singleSatCorrection.svid = static_cast<int32_t>(satId);
     singleSatCorrection.carrierFrequencyHz = carrierFreqHz;
     singleSatCorrection.probSatIsLos = probSatIsLos;
-    singleSatCorrection.excessPathLengthMeters = eplMeters;
-    singleSatCorrection.excessPathLengthUncertaintyMeters = eplUncMeters;
-    singleSatCorrection.reflectingPlane = reflectingPlane;
+    singleSatCorrection.combinedExcessPathLengthMeters = eplMeters;
+    singleSatCorrection.combinedExcessPathLengthUncertaintyMeters = eplUncMeters;
+    singleSatCorrection.combinedAttenuationDb = attenuationDb;
+    singleSatCorrection.excessPathInfos = excessPathInfos;
 
     return singleSatCorrection;
 }
@@ -391,8 +414,7 @@
         hardware::hidl_vec<SingleSatCorrection_V1_0>& list) {
     for (uint16_t i = 0; i < list.size(); ++i) {
         jobject singleSatCorrectionObj =
-                env->CallObjectMethod(singleSatCorrectionList, method_correctionListGet, i);
-
+                env->CallObjectMethod(singleSatCorrectionList, method_listGet, i);
         SingleSatCorrection_V1_0 singleSatCorrection =
                 getSingleSatCorrection_1_0_withoutConstellation(env, singleSatCorrectionObj);
 
@@ -410,7 +432,7 @@
         hardware::hidl_vec<SingleSatCorrection_V1_1>& list) {
     for (uint16_t i = 0; i < list.size(); ++i) {
         jobject singleSatCorrectionObj =
-                env->CallObjectMethod(singleSatCorrectionList, method_correctionListGet, i);
+                env->CallObjectMethod(singleSatCorrectionList, method_listGet, i);
 
         SingleSatCorrection_V1_0 singleSatCorrection_1_0 =
                 getSingleSatCorrection_1_0_withoutConstellation(env, singleSatCorrectionObj);
@@ -431,7 +453,7 @@
         JNIEnv* env, jobject singleSatCorrectionList, std::vector<SingleSatCorrection_Aidl>& list) {
     for (uint16_t i = 0; i < list.size(); ++i) {
         jobject singleSatCorrectionObj =
-                env->CallObjectMethod(singleSatCorrectionList, method_correctionListGet, i);
+                env->CallObjectMethod(singleSatCorrectionList, method_listGet, i);
 
         SingleSatCorrection_Aidl singleSatCorrection_Aidl =
                 getSingleSatCorrection_Aidl(env, singleSatCorrectionObj);
@@ -441,4 +463,63 @@
     }
 }
 
+template <>
+void MeasurementCorrectionsUtil::setReflectingPlaneAzimuthDegrees<ReflectingPlane_V1_0>(
+        ReflectingPlane_V1_0& reflectingPlane, double azimuthDegreeRefPlane) {
+    reflectingPlane.azimuthDegrees = azimuthDegreeRefPlane;
+}
+
+template <>
+void MeasurementCorrectionsUtil::setReflectingPlaneAzimuthDegrees<ReflectingPlane_Aidl>(
+        ReflectingPlane_Aidl& reflectingPlane, double azimuthDegreeRefPlane) {
+    reflectingPlane.reflectingPlaneAzimuthDegrees = azimuthDegreeRefPlane;
+}
+
+std::vector<ExcessPathInfo> MeasurementCorrectionsUtil::getExcessPathInfoList(
+        JNIEnv* env, jobject singleSatCorrectionObj) {
+    jobject excessPathInfoListObj =
+            env->CallObjectMethod(singleSatCorrectionObj, method_correctionSatExcessPathInfoList);
+
+    int len = env->CallIntMethod(excessPathInfoListObj, method_listSize);
+    std::vector<ExcessPathInfo> list(len);
+    for (int i = 0; i < len; ++i) {
+        jobject excessPathInfoObj = env->CallObjectMethod(excessPathInfoListObj, method_listGet, i);
+        list[i] = getExcessPathInfo(env, excessPathInfoObj);
+        env->DeleteLocalRef(excessPathInfoObj);
+    }
+    env->DeleteLocalRef(excessPathInfoListObj);
+    return list;
+}
+
+ExcessPathInfo MeasurementCorrectionsUtil::getExcessPathInfo(JNIEnv* env,
+                                                             jobject excessPathInfoObj) {
+    ExcessPathInfo excessPathInfo;
+    jint flags = env->CallIntMethod(excessPathInfoObj, method_excessPathInfoFlags);
+    excessPathInfo.excessPathInfoFlags = flags;
+    if ((flags & ExcessPathInfo::EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH) != 0) {
+        jfloat epl = env->CallFloatMethod(excessPathInfoObj, method_excessPathInfoEpl);
+        excessPathInfo.excessPathLengthMeters = epl;
+    }
+    if ((flags & ExcessPathInfo::EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH_UNC) != 0) {
+        jfloat eplUnc = env->CallFloatMethod(excessPathInfoObj, method_excessPathInfoEplUnc);
+        excessPathInfo.excessPathLengthUncertaintyMeters = eplUnc;
+    }
+    if ((flags & ExcessPathInfo::EXCESS_PATH_INFO_HAS_REFLECTING_PLANE) != 0) {
+        ReflectingPlane_Aidl reflectingPlane;
+        jobject reflectingPlaneObj =
+                env->CallObjectMethod(excessPathInfoObj, method_excessPathInfoRefPlane);
+        MeasurementCorrectionsUtil::setReflectingPlane<ReflectingPlane_Aidl>(env,
+                                                                             reflectingPlaneObj,
+                                                                             reflectingPlane);
+        env->DeleteLocalRef(reflectingPlaneObj);
+        excessPathInfo.reflectingPlane = reflectingPlane;
+    }
+    if ((flags & ExcessPathInfo::EXCESS_PATH_INFO_HAS_ATTENUATION) != 0) {
+        jfloat attenuation =
+                env->CallFloatMethod(excessPathInfoObj, method_excessPathInfoAttenuation);
+        excessPathInfo.attenuationDb = attenuation;
+    }
+    return excessPathInfo;
+}
+
 } // namespace android::gnss
diff --git a/services/core/jni/gnss/MeasurementCorrections.h b/services/core/jni/gnss/MeasurementCorrections.h
index a2e6027..598ad48 100644
--- a/services/core/jni/gnss/MeasurementCorrections.h
+++ b/services/core/jni/gnss/MeasurementCorrections.h
@@ -43,7 +43,7 @@
 extern jmethodID method_correctionsGetEnvironmentBearingDegrees;
 extern jmethodID method_correctionsGetEnvironmentBearingUncertaintyDegrees;
 extern jmethodID method_listSize;
-extern jmethodID method_correctionListGet;
+extern jmethodID method_listGet;
 extern jmethodID method_correctionSatFlags;
 extern jmethodID method_correctionSatConstType;
 extern jmethodID method_correctionSatId;
@@ -52,6 +52,7 @@
 extern jmethodID method_correctionSatEpl;
 extern jmethodID method_correctionSatEplUnc;
 extern jmethodID method_correctionSatRefPlane;
+extern jmethodID method_correctionSatExcessPathInfos;
 extern jmethodID method_correctionPlaneLatDeg;
 extern jmethodID method_correctionPlaneLngDeg;
 extern jmethodID method_correctionPlaneAltDeg;
@@ -130,14 +131,20 @@
     static bool translateMeasurementCorrections(JNIEnv* env, jobject correctionsObj,
                                                 T& corrections);
     template <class T>
-    static void getReflectingPlane(JNIEnv* env, jobject singleSatCorrectionObj, T& reflectingPlane);
+    static void setReflectingPlane(JNIEnv* env, jobject reflectingPlaneObj, T& reflectingPlane);
+    template <class T>
+    static void setReflectingPlaneAzimuthDegrees(T& reflectingPlane, double azimuthDegreeRefPlane);
+
+    static std::vector<
+            android::hardware::gnss::measurement_corrections::SingleSatCorrection::ExcessPathInfo>
+    getExcessPathInfoList(JNIEnv* env, jobject correctionsObj);
+    static android::hardware::gnss::measurement_corrections::SingleSatCorrection::ExcessPathInfo
+    getExcessPathInfo(JNIEnv* env, jobject correctionsObj);
 };
 
 template <class T>
-void MeasurementCorrectionsUtil::getReflectingPlane(JNIEnv* env, jobject singleSatCorrectionObj,
+void MeasurementCorrectionsUtil::setReflectingPlane(JNIEnv* env, jobject reflectingPlaneObj,
                                                     T& reflectingPlane) {
-    jobject reflectingPlaneObj =
-            env->CallObjectMethod(singleSatCorrectionObj, method_correctionSatRefPlane);
     jdouble latitudeDegreesRefPlane =
             env->CallDoubleMethod(reflectingPlaneObj, method_correctionPlaneLatDeg);
     jdouble longitudeDegreesRefPlane =
@@ -149,8 +156,7 @@
     reflectingPlane.latitudeDegrees = latitudeDegreesRefPlane;
     reflectingPlane.longitudeDegrees = longitudeDegreesRefPlane;
     reflectingPlane.altitudeMeters = altitudeDegreesRefPlane;
-    reflectingPlane.azimuthDegrees = azimuthDegreeRefPlane;
-    env->DeleteLocalRef(reflectingPlaneObj);
+    setReflectingPlaneAzimuthDegrees<T>(reflectingPlane, azimuthDegreeRefPlane);
 }
 
 } // namespace android::gnss
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 2f5ab0b..48c4052 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -1227,5 +1227,12 @@
 
         pw.print("mSsidDenylist=");
         pw.println(mSsidDenylist);
+
+        if (mFactoryResetProtectionPolicy != null) {
+            pw.println("mFactoryResetProtectionPolicy:");
+            pw.increaseIndent();
+            mFactoryResetProtectionPolicy.dump(pw);
+            pw.decreaseIndent();
+        }
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 200b120..ba00bee 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -189,4 +189,9 @@
     public ParcelableResource getString(String stringId) {
         return null;
     }
+
+    @Override
+    public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification() {
+        return false;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3d40f48..850fdee 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -18384,14 +18384,8 @@
             } else {
                 preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
             }
-            List<Integer> allowedUids = Arrays.stream(
-                    preferentialNetworkServiceConfig.getIncludedUids()).boxed().collect(
-                    Collectors.toList());
-            List<Integer> excludedUids = Arrays.stream(
-                    preferentialNetworkServiceConfig.getExcludedUids()).boxed().collect(
-                    Collectors.toList());
-            preferenceBuilder.setIncludedUids(allowedUids);
-            preferenceBuilder.setExcludedUids(excludedUids);
+            preferenceBuilder.setIncludedUids(preferentialNetworkServiceConfig.getIncludedUids());
+            preferenceBuilder.setExcludedUids(preferentialNetworkServiceConfig.getExcludedUids());
             preferenceBuilder.setPreferenceEnterpriseId(
                     preferentialNetworkServiceConfig.getNetworkId());
 
@@ -18790,4 +18784,18 @@
         mInjector.binderWithCleanCallingIdentity(() -> Settings.Secure.putInt(
                 mContext.getContentResolver(), MANAGED_PROVISIONING_DPC_DOWNLOADED, setTo));
     }
+
+    @Override
+    public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification() {
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLE_HOLDERS));
+        return mInjector.binderWithCleanCallingIdentity(() -> {
+            if (mUserManager.getUserCount() > 1) {
+                return false;
+            }
+            AccountManager am = AccountManager.get(mContext);
+            Account[] accounts = am.getAccounts();
+            return accounts.length == 0;
+        });
+    }
 }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index 8f81e93..7a9c412 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -143,6 +143,7 @@
         AndroidPackage::getLogo,
         AndroidPackage::getLocaleConfigRes,
         AndroidPackage::getManageSpaceActivityName,
+        AndroidPackage::getMaxSdkVersion,
         AndroidPackage::getMemtagMode,
         AndroidPackage::getMinSdkVersion,
         AndroidPackage::getNativeHeapZeroInitialized,
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 784f732..8d6269c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -118,6 +118,7 @@
         LocalServices.removeServiceForTest(LightsManager.class);
         LocalServices.addService(LightsManager.class, mMockedLightsManager);
         mInjector = new Injector();
+        when(mSurfaceControlProxy.getBootDisplayModeSupport()).thenReturn(true);
         mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler,
                 mListener, mInjector);
         spyOn(mAdapter);
@@ -904,7 +905,6 @@
                 .thenReturn(display.dynamicInfo);
         when(mSurfaceControlProxy.getDesiredDisplayModeSpecs(display.token))
                 .thenReturn(display.desiredDisplayModeSpecs);
-        when(mSurfaceControlProxy.getBootDisplayModeSupport()).thenReturn(true);
     }
 
     private void updateAvailableDisplays() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
index 6f503c7..444db91 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
@@ -19,7 +19,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
@@ -36,23 +38,21 @@
 import android.content.IntentFilter;
 import android.os.HandlerThread;
 import android.os.PowerManager;
-import android.util.ArraySet;
 
 import com.android.server.LocalServices;
 import com.android.server.PinnerService;
 import com.android.server.pm.dex.DexManager;
-import com.android.server.pm.dex.DexoptOptions;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.stream.Collectors;
@@ -66,9 +66,8 @@
 
     private static final long TEST_WAIT_TIMEOUT_MS = 10_000;
 
-    private static final ArraySet<String> DEFAULT_PACKAGE_LIST = new ArraySet<>(
-            Arrays.asList("aaa", "bbb"));
-    private static final ArraySet<String> EMPTY_PACKAGE_LIST = new ArraySet<>();
+    private static final List<String> DEFAULT_PACKAGE_LIST = List.of("aaa", "bbb");
+    private static final List<String> EMPTY_PACKAGE_LIST = List.of();
 
     @Mock
     private Context mContext;
@@ -116,9 +115,11 @@
         when(mInjector.getDataDirStorageLowBytes()).thenReturn(STORAGE_LOW_BYTES);
         when(mInjector.getDexOptThermalCutoff()).thenReturn(PowerManager.THERMAL_STATUS_CRITICAL);
         when(mInjector.getCurrentThermalStatus()).thenReturn(PowerManager.THERMAL_STATUS_NONE);
+        when(mInjector.supportSecondaryDex()).thenReturn(true);
         when(mDexOptHelper.getOptimizablePackages(any())).thenReturn(DEFAULT_PACKAGE_LIST);
         when(mDexOptHelper.performDexOptWithStatus(any())).thenReturn(
                 PackageDexOptimizer.DEX_OPT_PERFORMED);
+        when(mDexOptHelper.performDexOpt(any())).thenReturn(true);
 
         mService = new BackgroundDexOptService(mInjector);
     }
@@ -418,26 +419,16 @@
         verifyPerformDexOpt(DEFAULT_PACKAGE_LIST, totalJobRuns);
     }
 
-    private void verifyPerformDexOpt(ArraySet<String> pkgs, int expectedRuns) {
-        ArgumentCaptor<DexoptOptions> dexOptOptions = ArgumentCaptor.forClass(DexoptOptions.class);
-        verify(mDexOptHelper, atLeastOnce()).performDexOptWithStatus(dexOptOptions.capture());
-        HashMap<String, Integer> primaryPkgs = new HashMap<>(); // K: pkg, V: dexopt runs left
-        for (String pkg : pkgs) {
-            primaryPkgs.put(pkg, expectedRuns);
-        }
-
-        for (DexoptOptions opt : dexOptOptions.getAllValues()) {
-            assertThat(pkgs).contains(opt.getPackageName());
-            assertThat(opt.isDexoptOnlySecondaryDex()).isFalse();
-            Integer count = primaryPkgs.get(opt.getPackageName());
-            assertThat(count).isNotNull();
-            if (count == 1) {
-                primaryPkgs.remove(opt.getPackageName());
-            } else {
-                primaryPkgs.put(opt.getPackageName(), count - 1);
+    private void verifyPerformDexOpt(List<String> pkgs, int expectedRuns) {
+        InOrder inOrder = inOrder(mDexOptHelper);
+        for (int i = 0; i < expectedRuns; i++) {
+            for (String pkg : pkgs) {
+                inOrder.verify(mDexOptHelper, times(1)).performDexOptWithStatus(argThat((option) ->
+                        option.getPackageName().equals(pkg) && !option.isDexoptOnlySecondaryDex()));
+                inOrder.verify(mDexOptHelper, times(1)).performDexOpt(argThat((option) ->
+                        option.getPackageName().equals(pkg) && option.isDexoptOnlySecondaryDex()));
             }
         }
-        assertThat(primaryPkgs).isEmpty();
     }
 
     private static class StartAndWaitThread extends Thread {
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java
new file mode 100644
index 0000000..52cd29c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.utils;
+
+import static android.os.Trace.TRACE_TAG_APP;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.contains;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.matches;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+
+import android.os.Trace;
+import android.util.Slog;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.MockedVoidMethod;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link TimingsTraceAndSlog}.
+ *
+ * <p>Usage: {@code atest FrameworksMockingServicesTests:TimingsTraceAndSlogTest}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TimingsTraceAndSlogTest {
+
+    private static final String TAG = "TEST";
+
+    private MockitoSession mSession;
+
+    @Before
+    public final void startMockSession() {
+        mSession = mockitoSession()
+                .spyStatic(Slog.class)
+                .spyStatic(Trace.class)
+                .startMocking();
+    }
+
+    @After
+    public final void finishMockSession() {
+        mSession.finishMocking();
+    }
+
+    @Test
+    public void testDifferentThreads() throws Exception {
+        TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP);
+        // Should be able to log on the same thread
+        log.traceBegin("test");
+        log.traceEnd();
+        final List<String> errors = new ArrayList<>();
+        // Calling from a different thread should fail
+        Thread t = new Thread(() -> {
+            try {
+                log.traceBegin("test");
+                errors.add("traceBegin should fail on a different thread");
+            } catch (IllegalStateException expected) {
+            }
+            try {
+                log.traceEnd();
+                errors.add("traceEnd should fail on a different thread");
+            } catch (IllegalStateException expected) {
+            }
+            // Verify that creating a new log will work
+            TimingsTraceAndSlog log2 = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP);
+            log2.traceBegin("test");
+            log2.traceEnd();
+
+        });
+        t.start();
+        t.join();
+        assertThat(errors).isEmpty();
+    }
+
+    @Test
+    public void testGetUnfinishedTracesForDebug() {
+        TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP);
+        assertThat(log.getUnfinishedTracesForDebug()).isEmpty();
+
+        log.traceBegin("One");
+        assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder();
+
+        log.traceBegin("Two");
+        assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One", "Two").inOrder();
+
+        log.traceEnd();
+        assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder();
+
+        log.traceEnd();
+        assertThat(log.getUnfinishedTracesForDebug()).isEmpty();
+    }
+
+    @Test
+    public void testLogDuration() throws Exception {
+        TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP);
+        log.logDuration("logro", 42);
+        verify((MockedVoidMethod) () -> Slog.v(eq(TAG), contains("logro took to complete: 42ms")));
+    }
+
+    @Test
+    public void testOneLevel() throws Exception {
+        TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP);
+        log.traceBegin("test");
+        log.traceEnd();
+
+        verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "test"));
+        verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP));
+        verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("test took to complete: \\dms")));
+    }
+
+    @Test
+    public void testMultipleLevels() throws Exception {
+        TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP);
+        log.traceBegin("L1");
+        log.traceBegin("L2");
+        log.traceEnd();
+        log.traceEnd();
+
+        verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L1"));
+        verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L2"));
+        verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP), times(2)); // L1 and L2
+
+        verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L2 took to complete: \\d+ms")));
+        verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L1 took to complete: \\d+ms")));
+    }
+
+    @Test
+    public void testEndNoBegin() throws Exception {
+        TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP);
+        log.traceEnd();
+        verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP));
+        verify((MockedVoidMethod) () -> Slog.d(eq(TAG), anyString()), never());
+        verify((MockedVoidMethod) () -> Slog.w(TAG, "traceEnd called more times than traceBegin"));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index f3a0b7f..0780d21 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -45,13 +46,16 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.hardware.display.DisplayManagerInternal;
 import android.os.Looper;
+import android.view.DisplayInfo;
 import android.view.MagnificationSpec;
 import android.view.accessibility.MagnificationAnimationCallback;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.WindowManagerInternal;
@@ -113,6 +117,8 @@
 
     FullScreenMagnificationController mFullScreenMagnificationController;
 
+    public DisplayManagerInternal mDisplayManagerInternalMock = mock(DisplayManagerInternal.class);
+
     @Before
     public void setUp() {
         Looper looper = InstrumentationRegistry.getContext().getMainLooper();
@@ -125,6 +131,12 @@
         when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L);
         initMockWindowManager();
 
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalDensityDpi = 300;
+        doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+
         mFullScreenMagnificationController = new FullScreenMagnificationController(
                 mMockControllerCtx, new Object(), mRequestObserver, mScaleProvider);
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 1ebcbe1..8438afcf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4260,14 +4260,11 @@
         dpm.setPreferentialNetworkServiceConfigs(List.of(preferentialNetworkServiceConfigEnabled));
         assertThat(dpm.getPreferentialNetworkServiceConfigs().get(0)
                 .isEnabled()).isTrue();
-        List<Integer> includedList = new ArrayList<>();
-        includedList.add(1);
-        includedList.add(2);
         ProfileNetworkPreference preferenceDetails =
                 new ProfileNetworkPreference.Builder()
                         .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK)
                         .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1)
-                        .setIncludedUids(includedList)
+                        .setIncludedUids(new int[]{1, 2})
                         .build();
         List<ProfileNetworkPreference> preferences = new ArrayList<>();
         preferences.add(preferenceDetails);
@@ -4295,14 +4292,11 @@
         dpm.setPreferentialNetworkServiceConfigs(List.of(preferentialNetworkServiceConfigEnabled));
         assertThat(dpm.getPreferentialNetworkServiceConfigs().get(0)
                 .isEnabled()).isTrue();
-        List<Integer> excludedUids = new ArrayList<>();
-        excludedUids.add(1);
-        excludedUids.add(2);
         ProfileNetworkPreference preferenceDetails =
                 new ProfileNetworkPreference.Builder()
                         .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK)
                         .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1)
-                        .setExcludedUids(excludedUids)
+                        .setExcludedUids(new int[]{1, 2})
                         .build();
         List<ProfileNetworkPreference> preferences = new ArrayList<>();
         preferences.clear();
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
new file mode 100644
index 0000000..26a83a2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.display.DisplayManagerInternal;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ColorFadeTest {
+    private static final int DISPLAY_ID = 123;
+
+    private Context mContext;
+
+    @Mock private DisplayManagerInternal mDisplayManagerInternalMock;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+        mContext = getInstrumentation().getTargetContext();
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+    }
+
+    @Test
+    public void testPrepareColorFadeForInvalidDisplay() {
+        when(mDisplayManagerInternalMock.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(null);
+        ColorFade colorFade = new ColorFade(DISPLAY_ID);
+        assertFalse(colorFade.prepare(mContext, ColorFade.MODE_FADE));
+    }
+
+    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+        LocalServices.removeServiceForTest(clazz);
+        LocalServices.addService(clazz, mock);
+    }
+
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 31be33e..fd1536c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -43,6 +43,7 @@
 import static com.android.os.AtomsProto.DNDModeProto.ID_FIELD_NUMBER;
 import static com.android.os.AtomsProto.DNDModeProto.UID_FIELD_NUMBER;
 import static com.android.os.AtomsProto.DNDModeProto.ZEN_MODE_FIELD_NUMBER;
+import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -1611,6 +1612,35 @@
     }
 
     @Test
+    public void testAddAutomaticZenRule_beyondSystemLimit() {
+        for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) {
+            ScheduleInfo si = new ScheduleInfo();
+            si.startHour = i;
+            AutomaticZenRule zenRule = new AutomaticZenRule("name" + i,
+                    null,
+                    new ComponentName("android", "ScheduleConditionProvider"),
+                    ZenModeConfig.toScheduleConditionId(si),
+                    new ZenPolicy.Builder().build(),
+                    NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+            String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test");
+            assertNotNull(id);
+        }
+        try {
+            AutomaticZenRule zenRule = new AutomaticZenRule("name",
+                    null,
+                    new ComponentName("android", "ScheduleConditionProvider"),
+                    ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+                    new ZenPolicy.Builder().build(),
+                    NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+            String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test");
+            fail("allowed too many rules to be created");
+        } catch (IllegalArgumentException e) {
+            // yay
+        }
+
+    }
+
+    @Test
     public void testAddAutomaticZenRule_CA() {
         AutomaticZenRule zenRule = new AutomaticZenRule("name",
                 null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index f9aa4b1..9902e83 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -500,6 +500,23 @@
         return Pair.create(splitPrimaryActivity, splitSecondActivity);
     }
 
+    @Test
+    public void testMoveVisibleTaskToFront() {
+        final ActivityRecord activity = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true).build().getTopMostActivity();
+        final ActivityRecord translucentActivity = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true).build().getTopMostActivity();
+        assertTrue(activity.mVisibleRequested);
+
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+                false /* mockGetRootTask */);
+        starter.getIntent().setComponent(activity.mActivityComponent);
+        final int result = starter.setReason("testMoveVisibleTaskToFront").execute();
+
+        assertEquals(START_TASK_TO_FRONT, result);
+        assertEquals(1, activity.compareTo(translucentActivity));
+    }
+
     /**
      * Tests activity is cleaned up properly in a task mode violation.
      */
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index 50eefa0..c5117bb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -19,12 +19,12 @@
 
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
-import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ContentRecorder.KEY_RECORD_TASK_FEATURE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -40,17 +40,22 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
 import android.util.DisplayMetrics;
 import android.view.ContentRecordingSession;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+
 /**
  * Tests for the {@link ContentRecorder} class.
  *
@@ -62,17 +67,18 @@
 @RunWith(WindowTestRunner.class)
 public class ContentRecorderTests extends WindowTestsBase {
     private static final IBinder TEST_TOKEN = new RecordingTestToken();
-    private final ContentRecordingSession mDefaultSession =
+    private static IBinder sTaskWindowContainerToken;
+    private final ContentRecordingSession mDisplaySession =
             ContentRecordingSession.createDisplaySession(TEST_TOKEN);
+    private ContentRecordingSession mTaskSession;
     private static Point sSurfaceSize;
     private ContentRecorder mContentRecorder;
     private SurfaceControl mRecordedSurface;
+    // Handle feature flag.
+    private ConfigListener mConfigListener;
+    private CountDownLatch mLatch;
 
     @Before public void setUp() {
-        // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
-        // mirror.
-        setUpDefaultTaskDisplayAreaWindowToken();
-
         // GIVEN SurfaceControl can successfully mirror the provided surface.
         sSurfaceSize = new Point(
                 mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
@@ -84,12 +90,32 @@
                 sSurfaceSize.x, sSurfaceSize.y,
                 DisplayMetrics.DENSITY_140, new Surface(), VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR);
         final int displayId = virtualDisplay.getDisplay().getDisplayId();
-        mDefaultSession.setDisplayId(displayId);
-
         mWm.mRoot.onDisplayAdded(displayId);
-        final DisplayContent mVirtualDisplayContent = mWm.mRoot.getDisplayContent(displayId);
-        mContentRecorder = new ContentRecorder(mVirtualDisplayContent);
-        spyOn(mVirtualDisplayContent);
+        final DisplayContent virtualDisplayContent = mWm.mRoot.getDisplayContent(displayId);
+        mContentRecorder = new ContentRecorder(virtualDisplayContent);
+        spyOn(virtualDisplayContent);
+
+        // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
+        // record.
+        setUpDefaultTaskDisplayAreaWindowToken();
+        mDisplaySession.setDisplayId(displayId);
+
+        // GIVEN there is a window token associated with a task to record.
+        sTaskWindowContainerToken = setUpTaskWindowContainerToken(virtualDisplayContent);
+        mTaskSession = ContentRecordingSession.createTaskSession(sTaskWindowContainerToken);
+        mTaskSession.setDisplayId(displayId);
+
+        mConfigListener = new ConfigListener();
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                mContext.getMainExecutor(), mConfigListener);
+        mLatch = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_RECORD_TASK_FEATURE,
+                "true", true);
+    }
+
+    @After
+    public void teardown() {
+        DeviceConfig.removeOnPropertiesChangedListener(mConfigListener);
     }
 
     @Test
@@ -102,22 +128,74 @@
 
     @Test
     public void testUpdateRecording_display() {
-        mContentRecorder.setContentRecordingSession(mDefaultSession);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
         assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
     }
 
     @Test
-    public void testUpdateRecording_task() {
-        mDefaultSession.setContentToRecord(RECORD_CONTENT_TASK);
-        mContentRecorder.setContentRecordingSession(mDefaultSession);
+    public void testUpdateRecording_display_nullToken() {
+        ContentRecordingSession session = ContentRecordingSession.createDisplaySession(TEST_TOKEN);
+        session.setDisplayId(mDisplaySession.getDisplayId());
+        session.setTokenToRecord(null);
+        mContentRecorder.setContentRecordingSession(session);
         mContentRecorder.updateRecording();
         assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
     }
 
     @Test
+    public void testUpdateRecording_display_noWindowContainer() {
+        doReturn(null).when(
+                mWm.mWindowContextListenerController).getContainer(any());
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
+    }
+
+    @Test
+    public void testUpdateRecording_task_featureDisabled() {
+        mLatch = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_RECORD_TASK_FEATURE,
+                "false", false);
+        mContentRecorder.setContentRecordingSession(mTaskSession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
+    }
+
+    @Test
+    public void testUpdateRecording_task_featureEnabled() {
+        // Feature already enabled; don't need to again.
+        mContentRecorder.setContentRecordingSession(mTaskSession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+    }
+
+    @Test
+    public void testUpdateRecording_task_nullToken() {
+        ContentRecordingSession session = ContentRecordingSession.createTaskSession(
+                sTaskWindowContainerToken);
+        session.setDisplayId(mDisplaySession.getDisplayId());
+        session.setTokenToRecord(null);
+        mContentRecorder.setContentRecordingSession(session);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
+        // TODO(b/219761722) validate VirtualDisplay is torn down when can't set up task recording.
+    }
+
+    @Test
+    public void testUpdateRecording_task_noWindowContainer() {
+        // Use the window container token of the DisplayContent, rather than task.
+        ContentRecordingSession invalidTaskSession = ContentRecordingSession.createTaskSession(
+                new WindowContainer.RemoteToken(mDisplayContent));
+        mContentRecorder.setContentRecordingSession(invalidTaskSession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
+        // TODO(b/219761722) validate VirtualDisplay is torn down when can't set up task recording.
+    }
+
+    @Test
     public void testUpdateRecording_wasPaused() {
-        mContentRecorder.setContentRecordingSession(mDefaultSession);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
 
         mContentRecorder.pauseRecording();
@@ -126,16 +204,6 @@
     }
 
     @Test
-    public void testUpdateRecording_wasStopped() {
-        mContentRecorder.setContentRecordingSession(mDefaultSession);
-        mContentRecorder.updateRecording();
-
-        mContentRecorder.remove();
-        mContentRecorder.updateRecording();
-        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
-    }
-
-    @Test
     public void testOnConfigurationChanged_neverRecording() {
         mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT);
 
@@ -146,7 +214,7 @@
 
     @Test
     public void testOnConfigurationChanged_resizesSurface() {
-        mContentRecorder.setContentRecordingSession(mDefaultSession);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
         mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT);
 
@@ -158,7 +226,7 @@
 
     @Test
     public void testPauseRecording_pausesRecording() {
-        mContentRecorder.setContentRecordingSession(mDefaultSession);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
 
         mContentRecorder.pauseRecording();
@@ -173,7 +241,7 @@
 
     @Test
     public void testRemove_stopsRecording() {
-        mContentRecorder.setContentRecordingSession(mDefaultSession);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
 
         mContentRecorder.remove();
@@ -188,8 +256,9 @@
 
     @Test
     public void testUpdateMirroredSurface_capturedAreaResized() {
-        mContentRecorder.setContentRecordingSession(mDefaultSession);
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
 
         // WHEN attempting to mirror on the virtual display, and the captured content is resized.
         float xScale = 0.7f;
@@ -197,13 +266,14 @@
         Rect displayAreaBounds = new Rect(0, 0, Math.round(sSurfaceSize.x * xScale),
                 Math.round(sSurfaceSize.y * yScale));
         mContentRecorder.updateMirroredSurface(mTransaction, displayAreaBounds, sSurfaceSize);
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
 
         // THEN content in the captured DisplayArea is scaled to fit the surface size.
         verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, 1.0f / yScale, 0, 0,
                 1.0f / yScale);
         // THEN captured content is positioned in the centre of the output surface.
-        float scaledWidth = displayAreaBounds.width() / xScale;
-        float xInset = (sSurfaceSize.x - scaledWidth) / 2;
+        int scaledWidth = Math.round((float) displayAreaBounds.width() / xScale);
+        int xInset = (sSurfaceSize.x - scaledWidth) / 2;
         verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, xInset, 0);
     }
 
@@ -222,6 +292,18 @@
     }
 
     /**
+     * Creates a {@link android.window.WindowContainerToken} associated with a task, in order for
+     * that task to be recorded.
+     */
+    private IBinder setUpTaskWindowContainerToken(DisplayContent displayContent) {
+        final Task rootTask = createTask(displayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+        // Ensure the task is not empty.
+        createActivityRecord(displayContent, task);
+        return task.getTaskInfo().token.asBinder();
+    }
+
+    /**
      * SurfaceControl successfully creates a mirrored surface of the given size.
      */
     private SurfaceControl surfaceControlMirrors(Point surfaceSize) {
@@ -236,4 +318,13 @@
                 anyInt());
         return mirroredSurface;
     }
+
+    private class ConfigListener implements DeviceConfig.OnPropertiesChangedListener {
+        @Override
+        public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
+            if (mLatch != null && properties.getKeyset().contains(KEY_RECORD_TASK_FEATURE)) {
+                mLatch.countDown();
+            }
+        }
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 25d7334..b4d305b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -932,7 +932,7 @@
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         assertNotNull(o.mChangedInfo);
         assertNotNull(o.mChangedInfo.pictureInPictureParams);
-        final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatioRational();
+        final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatio();
         assertEquals(3, ratio.getNumerator());
         assertEquals(4, ratio.getDenominator());
     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 837cf8b..cff90bb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -504,7 +504,12 @@
     /** Control whether users can choose a network operator. */
     public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
 
-    /** Used in Cellular Network Settings for preferred network type. */
+    /**
+     * Used in the Preferred Network Types menu to determine if the 2G option is displayed.
+     * Value defaults to false as of Android T to discourage the use of insecure 2G protocols.
+     *
+     * @see #KEY_HIDE_ENABLE_2G
+     */
     public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
 
     /**
@@ -8594,7 +8599,7 @@
         sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
         sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
         sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
-        sDefaults.putBoolean(KEY_PREFER_2G_BOOL, true);
+        sDefaults.putBoolean(KEY_PREFER_2G_BOOL, false);
         sDefaults.putBoolean(KEY_4G_ONLY_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_APN_SETTING_CDMA_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_CDMA_CHOICES_BOOL, false);
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index d2a4c3e..3a3b363 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -1090,13 +1090,6 @@
      */
     public static final int NO_RETRY_FAILURE = 0x1000B;
 
-    /**
-     * Traffic descriptors in DataCallResponse is empty.
-     *
-     * @hide
-     */
-    public static final int NO_TRAFFIC_DESCRIPTORS = 0x1000C;
-
     private static final Map<Integer, String> sFailCauseMap;
     static {
         sFailCauseMap = new HashMap<>();
@@ -1531,7 +1524,6 @@
         sFailCauseMap.put(SERVICE_TEMPORARILY_UNAVAILABLE, "SERVICE_TEMPORARILY_UNAVAILABLE");
         sFailCauseMap.put(REQUEST_NOT_SUPPORTED, "REQUEST_NOT_SUPPORTED");
         sFailCauseMap.put(NO_RETRY_FAILURE, "NO_RETRY_FAILURE");
-        sFailCauseMap.put(NO_TRAFFIC_DESCRIPTORS, "NO_TRAFFIC_DESCRIPTORS");
     }
 
     private DataFailCause() {
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 532679c..8143da5 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -549,7 +549,6 @@
      * Returns the profile id to which the APN saved in modem.
      *
      * @return the profile id of the APN
-     * @hide
      */
     public int getProfileId() {
         return mProfileId;
@@ -558,8 +557,7 @@
     /**
      * Returns if the APN setting is persistent on the modem.
      *
-     * @return is the APN setting to be set in modem
-     * @hide
+     * @return {@code true} if the APN setting is persistent on the modem.
      */
     public boolean isPersistent() {
         return mPersistent;
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 892eb29..bd346d5 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -31,6 +31,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -166,7 +167,8 @@
          *        link properties of the existing data connection, otherwise null.
          * @param callback The result callback for this request.
          */
-        public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile,
+        public void setupDataCall(
+                @RadioAccessNetworkType int accessNetworkType, @NonNull DataProfile dataProfile,
                 boolean isRoaming, boolean allowRoaming,
                 @SetupDataReason int reason, @Nullable LinkProperties linkProperties,
                 @NonNull DataServiceCallback callback) {
@@ -214,7 +216,8 @@
          *        for example, a zero-rating slice.
          * @param callback The result callback for this request.
          */
-        public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile,
+        public void setupDataCall(
+                @RadioAccessNetworkType int accessNetworkType, @NonNull DataProfile dataProfile,
                 boolean isRoaming, boolean allowRoaming,
                 @SetupDataReason int reason,
                 @Nullable LinkProperties linkProperties,
@@ -294,6 +297,9 @@
          * with reason {@link DataService.REQUEST_REASON_HANDOVER}. The target transport now owns
          * the transferred resources and is responsible for releasing them.
          *
+         * <p/>
+         * Note that the callback will be executed on binder thread.
+         *
          * @param cid The identifier of the data call which is provided in {@link DataCallResponse}
          * @param callback The result callback for this request.
          *
@@ -322,6 +328,9 @@
          * </li>
          * </ul>
          *
+         * <p/>
+         * Note that the callback will be executed on binder thread.
+         *
          * @param cid The identifier of the data call which is provided in {@link DataCallResponse}
          * @param callback The result callback for this request.
          *
diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
index fe44530..5ffee56 100644
--- a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
+++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
@@ -32,7 +32,12 @@
 import java.util.Objects;
 
 /**
- * Provides Qos attributes of an EPS bearer.
+ * Provides QOS attributes of an EPS bearer.
+ *
+ * <p> The dedicated EPS bearer along with QOS is allocated by the LTE network and notified to the
+ * device. The Telephony framework creates the {@link EpsBearerQosSessionAttributes} object which
+ * represents the QOS of the dedicated bearer and notifies the same to applications via
+ * {@link QosCallback}.
  *
  * {@hide}
  */
diff --git a/tools/traceinjection/Android.bp b/tools/traceinjection/Android.bp
new file mode 100644
index 0000000..1395c5f
--- /dev/null
+++ b/tools/traceinjection/Android.bp
@@ -0,0 +1,49 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_binary_host {
+    name: "traceinjection",
+    manifest: "manifest.txt",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "asm-7.0",
+        "asm-commons-7.0",
+        "asm-tree-7.0",
+        "asm-analysis-7.0",
+        "guava-21.0",
+    ],
+}
+
+java_library_host {
+    name: "TraceInjectionTests-Uninjected",
+    srcs: ["test/**/*.java"],
+    static_libs: [
+        "junit",
+    ],
+}
+
+java_genrule_host {
+    name: "TraceInjectionTests-Injected",
+    srcs: [":TraceInjectionTests-Uninjected"],
+    tools: ["traceinjection"],
+    cmd: "$(location traceinjection) " +
+        "  --annotation \"com/android/traceinjection/Trace\"" +
+        "  --start \"com/android/traceinjection/InjectionTests.traceStart\"" +
+        "  --end \"com/android/traceinjection/InjectionTests.traceEnd\"" +
+        "  -o $(out) " +
+        "  -i $(in)",
+    out: ["TraceInjectionTests-Injected.jar"],
+}
+
+java_test_host {
+    name: "TraceInjectionTests",
+    static_libs: [
+        "TraceInjectionTests-Injected",
+    ],
+}
diff --git a/tools/traceinjection/manifest.txt b/tools/traceinjection/manifest.txt
new file mode 100644
index 0000000..7f4ee1d
--- /dev/null
+++ b/tools/traceinjection/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.traceinjection.Main
diff --git a/tools/traceinjection/src/com/android/traceinjection/Main.java b/tools/traceinjection/src/com/android/traceinjection/Main.java
new file mode 100644
index 0000000..190df81
--- /dev/null
+++ b/tools/traceinjection/src/com/android/traceinjection/Main.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.traceinjection;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.BufferedInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public class Main {
+    public static void main(String[] args) throws IOException {
+        String inJar = null;
+        String outJar = null;
+        String annotation = null;
+        String traceStart = null;
+        String traceEnd = null;
+
+        // All arguments require a value currently, so just make sure we have an even number and
+        // then process them all two at a time.
+        if (args.length % 2 != 0) {
+            throw new IllegalArgumentException("Argument is missing corresponding value");
+        }
+        for (int i = 0; i < args.length - 1; i += 2) {
+            final String arg = args[i].trim();
+            final String argValue = args[i + 1].trim();
+            if ("-i".equals(arg)) {
+                inJar = argValue;
+            } else if ("-o".equals(arg)) {
+                outJar = argValue;
+            } else if ("--annotation".equals(arg)) {
+                annotation = argValue;
+            } else if ("--start".equals(arg)) {
+                traceStart = argValue;
+            } else if ("--end".equals(arg)) {
+                traceEnd = argValue;
+            } else {
+                throw new IllegalArgumentException("Unknown argument: " + arg);
+            }
+        }
+
+        if (inJar == null) {
+            throw new IllegalArgumentException("input jar is required");
+        }
+
+        if (outJar == null) {
+            throw new IllegalArgumentException("output jar is required");
+        }
+
+        if (annotation == null) {
+            throw new IllegalArgumentException("trace annotation is required");
+        }
+
+        if (traceStart == null) {
+            throw new IllegalArgumentException("start trace method is required");
+        }
+
+        if (traceEnd == null) {
+            throw new IllegalArgumentException("end trace method is required");
+        }
+
+        TraceInjectionConfiguration params =
+                new TraceInjectionConfiguration(annotation, traceStart, traceEnd);
+
+        try (
+                ZipFile zipSrc = new ZipFile(inJar);
+                ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outJar));
+        ) {
+            Enumeration<? extends ZipEntry> srcEntries = zipSrc.entries();
+            while (srcEntries.hasMoreElements()) {
+                ZipEntry entry = srcEntries.nextElement();
+                ZipEntry newEntry = new ZipEntry(entry.getName());
+                newEntry.setTime(entry.getTime());
+                zos.putNextEntry(newEntry);
+                BufferedInputStream bis = new BufferedInputStream(zipSrc.getInputStream(entry));
+
+                if (entry.getName().endsWith(".class")) {
+                    convert(bis, zos, params);
+                } else {
+                    while (bis.available() > 0) {
+                        zos.write(bis.read());
+                    }
+                    zos.closeEntry();
+                    bis.close();
+                }
+            }
+            zos.finish();
+        }
+    }
+
+    private static void convert(InputStream in, OutputStream out,
+            TraceInjectionConfiguration params) throws IOException {
+        ClassReader cr = new ClassReader(in);
+        ClassWriter cw = new ClassWriter(0);
+        TraceInjectionClassVisitor cv = new TraceInjectionClassVisitor(cw, params);
+        cr.accept(cv, ClassReader.EXPAND_FRAMES);
+        byte[] data = cw.toByteArray();
+        out.write(data);
+    }
+}
diff --git a/tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java
new file mode 100644
index 0000000..863f976
--- /dev/null
+++ b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionClassVisitor.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.traceinjection;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * {@link ClassVisitor} that injects tracing code to methods annotated with the configured
+ * annotation.
+ */
+public class TraceInjectionClassVisitor extends ClassVisitor {
+    private final TraceInjectionConfiguration mParams;
+    public TraceInjectionClassVisitor(ClassVisitor classVisitor,
+            TraceInjectionConfiguration params) {
+        super(Opcodes.ASM7, classVisitor);
+        mParams = params;
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+            String[] exceptions) {
+        MethodVisitor chain = super.visitMethod(access, name, desc, signature, exceptions);
+        return new TraceInjectionMethodAdapter(chain, access, name, desc, mParams);
+    }
+}
diff --git a/tools/traceinjection/src/com/android/traceinjection/TraceInjectionConfiguration.java b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionConfiguration.java
new file mode 100644
index 0000000..f9595bd
--- /dev/null
+++ b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionConfiguration.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.traceinjection;
+
+/**
+ * Configuration data for trace method injection.
+ */
+public class TraceInjectionConfiguration {
+    public final String annotation;
+    public final String startMethodClass;
+    public final String startMethodName;
+    public final String endMethodClass;
+    public final String endMethodName;
+
+    public TraceInjectionConfiguration(String annotation, String startMethod, String endMethod) {
+        this.annotation = annotation;
+        String[] startMethodComponents = parseMethod(startMethod);
+        String[] endMethodComponents = parseMethod(endMethod);
+        startMethodClass = startMethodComponents[0];
+        startMethodName = startMethodComponents[1];
+        endMethodClass = endMethodComponents[0];
+        endMethodName = endMethodComponents[1];
+    }
+
+    public String toString() {
+        return "TraceInjectionParams{annotation=" + annotation
+                + ", startMethod=" + startMethodClass + "." + startMethodName
+                + ", endMethod=" + endMethodClass + "." + endMethodName + "}";
+    }
+
+    private static String[] parseMethod(String method) {
+        String[] methodComponents = method.split("\\.");
+        if (methodComponents.length != 2) {
+            throw new IllegalArgumentException("Invalid method descriptor: " + method);
+        }
+        return methodComponents;
+    }
+}
diff --git a/tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java
new file mode 100644
index 0000000..c2bbddc
--- /dev/null
+++ b/tools/traceinjection/src/com/android/traceinjection/TraceInjectionMethodAdapter.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.traceinjection;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.AdviceAdapter;
+import org.objectweb.asm.commons.Method;
+
+/**
+ * Adapter that injects tracing code to methods annotated with the configured annotation.
+ *
+ * Assuming the configured annotation is {@code @Trace} and the configured methods are
+ * {@code Tracing.begin()} and {@code Tracing.end()}, it effectively transforms:
+ *
+ * <pre>{@code
+ * @Trace
+ * void method() {
+ *     doStuff();
+ * }
+ * }</pre>
+ *
+ * into:
+ * <pre>{@code
+ * @Trace
+ * void method() {
+ *     Tracing.begin();
+ *     try {
+ *         doStuff();
+ *     } finally {
+ *         Tracing.end();
+ *     }
+ * }
+ * }</pre>
+ */
+public class TraceInjectionMethodAdapter extends AdviceAdapter {
+    private final TraceInjectionConfiguration mParams;
+    private final Label mStartFinally = newLabel();
+    private final boolean mIsConstructor;
+
+    private boolean mShouldTrace;
+    private long mTraceId;
+    private String mTraceLabel;
+
+    public TraceInjectionMethodAdapter(MethodVisitor methodVisitor, int access,
+            String name, String descriptor, TraceInjectionConfiguration params) {
+        super(Opcodes.ASM7, methodVisitor, access, name, descriptor);
+        mParams = params;
+        mIsConstructor = "<init>".equals(name);
+    }
+
+    @Override
+    public void visitCode() {
+        super.visitCode();
+        if (mShouldTrace) {
+            visitLabel(mStartFinally);
+        }
+    }
+
+    @Override
+    protected void onMethodEnter() {
+        if (!mShouldTrace) {
+            return;
+        }
+        Type type = Type.getType(toJavaSpecifier(mParams.startMethodClass));
+        Method trace = Method.getMethod("void " + mParams.startMethodName + " (long, String)");
+        push(mTraceId);
+        push(getTraceLabel());
+        invokeStatic(type, trace);
+    }
+
+    private String getTraceLabel() {
+        return !isEmpty(mTraceLabel) ? mTraceLabel : getName();
+    }
+
+    @Override
+    protected void onMethodExit(int opCode) {
+        // Any ATHROW exits will be caught as part of our exception-handling block, so putting it
+        // here would cause us to call the end trace method multiple times.
+        if (opCode != ATHROW) {
+            onFinally();
+        }
+    }
+
+    private void onFinally() {
+        if (!mShouldTrace) {
+            return;
+        }
+        Type type = Type.getType(toJavaSpecifier(mParams.endMethodClass));
+        Method trace = Method.getMethod("void " + mParams.endMethodName + " (long)");
+        push(mTraceId);
+        invokeStatic(type, trace);
+    }
+
+    @Override
+    public void visitMaxs(int maxStack, int maxLocals) {
+        final int minStackSize;
+        if (mShouldTrace) {
+            Label endFinally = newLabel();
+            visitLabel(endFinally);
+            catchException(mStartFinally, endFinally, null);
+            // The stack will always contain exactly one element: the exception we caught
+            final Object[] stack = new Object[]{ "java/lang/Throwable"};
+            // Because we use EXPAND_FRAMES, the frame type must always be F_NEW.
+            visitFrame(F_NEW, /* numLocal= */ 0, /* local= */ null, stack.length, stack);
+            onFinally();
+            // Rethrow the exception that we caught in the finally block.
+            throwException();
+
+            // Make sure we have at least enough stack space to push the trace arguments
+            // (long, String)
+            minStackSize = Type.LONG_TYPE.getSize() + Type.getType(String.class).getSize();
+        } else {
+            // We didn't inject anything, so no need for additional stack space.
+            minStackSize = 0;
+        }
+
+        super.visitMaxs(Math.max(minStackSize, maxStack), maxLocals);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+        AnnotationVisitor av = super.visitAnnotation(descriptor, visible);
+        if (descriptor.equals(toJavaSpecifier(mParams.annotation))) {
+            if (mIsConstructor) {
+                // TODO: Support constructor tracing. At the moment, constructors aren't supported
+                //  because you can't put an exception handler around a super() call within the
+                //  constructor itself.
+                throw new IllegalStateException("Cannot trace constructors");
+            }
+            av = new TracingAnnotationVisitor(av);
+        }
+        return av;
+    }
+
+    /**
+     * An AnnotationVisitor that pulls the trace ID and label information from the configured
+     * annotation.
+     */
+    class TracingAnnotationVisitor extends AnnotationVisitor {
+
+        TracingAnnotationVisitor(AnnotationVisitor annotationVisitor) {
+            super(Opcodes.ASM7, annotationVisitor);
+        }
+
+        @Override
+        public void visit(String name, Object value) {
+            if ("tag".equals(name)) {
+                mTraceId = (long) value;
+                // If we have a trace annotation and ID, then we have everything we need to trace
+                mShouldTrace = true;
+            } else if ("label".equals(name)) {
+                mTraceLabel = (String) value;
+            }
+            super.visit(name, value);
+        }
+    }
+
+    private static String toJavaSpecifier(String klass) {
+        return "L" + klass + ";";
+    }
+
+    private static boolean isEmpty(String str) {
+        return str == null || "".equals(str);
+    }
+}
diff --git a/tools/traceinjection/test/com/android/traceinjection/InjectionTests.java b/tools/traceinjection/test/com/android/traceinjection/InjectionTests.java
new file mode 100644
index 0000000..81bf235
--- /dev/null
+++ b/tools/traceinjection/test/com/android/traceinjection/InjectionTests.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.traceinjection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(JUnit4.class)
+public class InjectionTests {
+    public static final int TRACE_TAG = 42;
+    public static final String CUSTOM_TRACE_NAME = "Custom";
+
+    public static final TraceTracker TRACKER = new TraceTracker();
+
+    @After
+    public void tearDown() {
+        TRACKER.reset();
+    }
+
+    @Test
+    public void testDefaultLabel() {
+        assertTraces(this::tracedMethod, "tracedMethod");
+        tracedMethodThrowsAndCatches();
+    }
+
+    @Test
+    public void testCustomLabel() {
+        assertTraces(this::tracedMethodHasCustomName, CUSTOM_TRACE_NAME);
+    }
+
+    @Test
+    public void testTracedMethodsStillThrow() {
+        assertTraces(() -> assertThrows(IllegalArgumentException.class, this::tracedMethodThrows),
+                "tracedMethodThrows");
+        // Also test that we rethrow exceptions from method calls. This is slightly different from
+        // the previous case because the ATHROW instruction is not actually present at all in the
+        // bytecode of the instrumented method.
+        TRACKER.reset();
+        assertTraces(() -> assertThrows(NullPointerException.class,
+                        this::tracedMethodCallsThrowingMethod),
+                "tracedMethodCallsThrowingMethod");
+    }
+
+    @Test
+    public void testNestedTracedMethods() {
+        assertTraces(this::outerTracedMethod, "outerTracedMethod", "innerTracedMethod");
+    }
+
+    @Test
+    public void testTracedMethodWithCatchBlock() {
+        assertTraces(this::tracedMethodThrowsAndCatches, "tracedMethodThrowsAndCatches");
+    }
+
+    @Test
+    public void testTracedMethodWithFinallyBlock() {
+        assertTraces(() -> assertThrows(IllegalArgumentException.class,
+                this::tracedMethodThrowWithFinally), "tracedMethodThrowWithFinally");
+    }
+
+    @Test
+    public void testNonVoidMethod() {
+        assertTraces(this::tracedNonVoidMethod, "tracedNonVoidMethod");
+    }
+
+    @Test
+    public void testNonVoidMethodReturnsWithinCatches() {
+        assertTraces(this::tracedNonVoidMethodReturnsWithinCatches,
+                "tracedNonVoidMethodReturnsWithinCatches");
+    }
+
+    @Test
+    public void testNonVoidMethodReturnsWithinFinally() {
+        assertTraces(this::tracedNonVoidMethodReturnsWithinFinally,
+                "tracedNonVoidMethodReturnsWithinFinally");
+    }
+
+    @Test
+    public void testTracedStaticMethod() {
+        assertTraces(InjectionTests::tracedStaticMethod, "tracedStaticMethod");
+    }
+
+    @Trace(tag = TRACE_TAG)
+    public void tracedMethod() {
+        assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+    }
+
+    @Trace(tag = TRACE_TAG)
+    public void tracedMethodThrows() {
+        throw new IllegalArgumentException();
+    }
+
+    @Trace(tag = TRACE_TAG)
+    public void tracedMethodCallsThrowingMethod() {
+        throwingMethod();
+    }
+
+    private void throwingMethod() {
+        throw new NullPointerException();
+    }
+
+
+    @Trace(tag = TRACE_TAG)
+    public void tracedMethodThrowsAndCatches() {
+        try {
+            throw new IllegalArgumentException();
+        } catch (IllegalArgumentException ignored) {
+            assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+        }
+    }
+
+    @Trace(tag = TRACE_TAG)
+    public void tracedMethodThrowWithFinally() {
+        try {
+            throw new IllegalArgumentException();
+        } finally {
+            assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+        }
+    }
+
+    @Trace(tag = TRACE_TAG, label = CUSTOM_TRACE_NAME)
+    public void tracedMethodHasCustomName() {
+    }
+
+    @Trace(tag = TRACE_TAG)
+    public void outerTracedMethod() {
+        innerTracedMethod();
+        assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+    }
+
+    @Trace(tag = TRACE_TAG)
+    public void innerTracedMethod() {
+        assertEquals(2, TRACKER.getTraceCount(TRACE_TAG));
+    }
+
+    @Trace(tag = TRACE_TAG)
+    public int tracedNonVoidMethod() {
+        assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+        return 0;
+    }
+
+    @Trace(tag = TRACE_TAG)
+    public int tracedNonVoidMethodReturnsWithinCatches() {
+        try {
+            throw new IllegalArgumentException();
+        } catch (IllegalArgumentException ignored) {
+            assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+            return 0;
+        }
+    }
+
+    @Trace(tag = TRACE_TAG)
+    public int tracedNonVoidMethodReturnsWithinFinally() {
+        try {
+            throw new IllegalArgumentException();
+        } finally {
+            assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+            return 0;
+        }
+    }
+
+    @Trace(tag = TRACE_TAG)
+    public static void tracedStaticMethod() {
+        assertEquals(1, TRACKER.getTraceCount(TRACE_TAG));
+    }
+
+    public void assertTraces(Runnable r, String... traceLabels) {
+        r.run();
+        assertEquals(Arrays.asList(traceLabels), TRACKER.getTraceLabels(TRACE_TAG));
+        TRACKER.assertAllTracesClosed();
+    }
+
+    public static void traceStart(long tag, String name) {
+        TRACKER.onTraceStart(tag, name);
+    }
+
+    public static void traceEnd(long tag) {
+        TRACKER.onTraceEnd(tag);
+    }
+
+    static class TraceTracker {
+        private final Map<Long, List<String>> mTraceLabelsByTag = new HashMap<>();
+        private final Map<Long, Integer> mTraceCountsByTag = new HashMap<>();
+
+        public void onTraceStart(long tag, String name) {
+            getTraceLabels(tag).add(name);
+            mTraceCountsByTag.put(tag, mTraceCountsByTag.getOrDefault(tag, 0) + 1);
+        }
+
+        public void onTraceEnd(long tag) {
+            final int newCount = getTraceCount(tag) - 1;
+            if (newCount < 0) {
+                throw new IllegalStateException("Trace count has gone negative for tag " + tag);
+            }
+            mTraceCountsByTag.put(tag, newCount);
+        }
+
+        public void reset() {
+            mTraceLabelsByTag.clear();
+            mTraceCountsByTag.clear();
+        }
+
+        public List<String> getTraceLabels(long tag) {
+            if (!mTraceLabelsByTag.containsKey(tag)) {
+                mTraceLabelsByTag.put(tag, new ArrayList<>());
+            }
+            return mTraceLabelsByTag.get(tag);
+        }
+
+        public int getTraceCount(long tag) {
+            return mTraceCountsByTag.getOrDefault(tag, 0);
+        }
+
+        public void assertAllTracesClosed() {
+            for (Map.Entry<Long, Integer> count: mTraceCountsByTag.entrySet()) {
+                final String errorMsg = "Tag " + count.getKey() + " is not fully closed (count="
+                        + count.getValue() + ")";
+                assertEquals(errorMsg, 0, (int) count.getValue());
+            }
+        }
+    }
+}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl b/tools/traceinjection/test/com/android/traceinjection/Trace.java
similarity index 76%
copy from media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
copy to tools/traceinjection/test/com/android/traceinjection/Trace.java
index 5e15016..9e1c545 100644
--- a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
+++ b/tools/traceinjection/test/com/android/traceinjection/Trace.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.media.tv.interactive;
+package com.android.traceinjection;
 
-parcelable TvInteractiveAppInfo;
\ No newline at end of file
+public @interface Trace {
+    long tag();
+    String label() default "";
+}